useReducer()
React에서 컴포넌트의 상태 관리를 위해서 useState를 사용해서 상태를 업데이트를 하는데, useReducer를 사용하게 되면 컴포넌트와 상태 업데이트 로직을 분리하여 컴포넌트 외부에서도 상태 관리를 할 수 있다.
즉, 현재 컴포넌트가 아닌 다른 곳에 state를 저장하고 싶을 때 유용하게 사용할 수 있다.
원래는 컴포넌트와 상태변수를 같이뒀었다.
부모 자식 관계가 복잡한 상태에서 상태변수를 서로 공유하면서 쓰려면 복잡하다. (전달 전달 전달해서 줘야하므로)
그러므로 데이터 공유 지역을 따로 만들어서 거기에 데이터를 보관하는 것이다.
그리고 필요할 때 갖다쓰라는 것이다.
컴포넌트와 변수를 따로따로 관리하자는 것이다. (컴포넌트와 별개의 개념으로 들어가야한다.)
이런 Redux를 하려면 useReducer( )가 필요하다.
useState / useReducer를 알아야 Redux를 할 수가 있다!!
[사용법]
const [state, dispatch] = useReducer(reducer, initialState);
state : 현재 상태
dispatch : action을 발생시키는 함수
reducer : state와 action를 받아 새로운 state를 반환하는 함수
initialState : 초기값
day06
Test01.jsx
import React from 'react';
import { useState } from 'react';
const Test01 = () => {
const [count, setCount] = useState(0);
return (
<div>
<h1>카운트 : { count }</h1>
<p>
<button onClick={ () => setCount(count + 1) }>증가</button>
<button>감소</button>
<button>초기화</button>
</p>
</div>
);
};
export default Test01;
지금 머리 위에 적는 건 나중에 외부파일로 빼면 된다 생각하기
initialState : 초기값
//초기값
const initialState = 0;
reducer : state와 action를 받아 새로운 state를 반환하는 함수
//함수
const reducer = () => {파라메터로 2개의 값을 받는다. 상태변수, 함수}
const reducer = (state, action) => {}
const [state, dispatch] = useReducer(reducer, initialState);
const [count, dispatch] = useReducer(reducer, initialState);
reducer라는 함수를 부르는데 초기값은 initialState이다.
count => state / dispatch => action
(상태변수와 이벤트발생시키는 함수 가지고 reducer로 가는 것 !!)
dispatch : action을 발생시키는 함수
<button onClick={ () => dispatch({ type: 'INCREMENT' }) }>증가</button>
위에있는 타입을 받은 reducer 함수
//함수
const reducer = (state, action) => {
switch(action.type){
case 'INCREMENT' :
return state + 1
}
<p>
<button onClick={ () => dispatch({ type: 'INCREMENT' }) }>증가</button>
<button onClick={ () => dispatch({ type: 'DECREMENT' }) }>감소</button>
<button onClick={ () => dispatch({ type: 'RESET' }) }>초기화</button>
</p>
//함수
const reducer = (state, action) => {
switch(action.type){
case 'INCREMENT' :
return state + 1
case 'DECREMENT' :
return state - 1
case 'RESET' :
return 0
default :
return state //default는 반드시 작성해줘야한다.
}
}
default는 반드시 작성해줘야하는 거 !!
reducer를 통해서 외부에 있는 상태변수의 값을 증가, 감소, 초기화할 수 있다는 것 !!
그리고 return을 통해 그 값을 다시 받는다는 것 체크 !
전체코드
import React from 'react';
import { useReducer } from 'react';
import { useState } from 'react';
//초기값
const initialState = 0;
//함수
const reducer = (state, action) => {
switch(action.type){
case 'INCREMENT' :
return state + 1
case 'DECREMENT' :
return state - 1
case 'RESET' :
return 0
default :
return state //default는 반드시 작성해줘야한다.
}
}
const Test01 = () => {
//count => state / dispatch => action
const [count, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1>카운트 : { count }</h1>
<p>
<button onClick={ () => dispatch({ type: 'INCREMENT' }) }>증가</button>
<button onClick={ () => dispatch({ type: 'DECREMENT' }) }>감소</button>
<button onClick={ () => dispatch({ type: 'RESET' }) }>초기화</button>
</p>
</div>
);
};
export default Test01;
Test02.jsx
import React from 'react';
import { useReducer } from 'react';
const initialState = 'pink';
const reducer = (state, action) => {
switch(action.type){
case 'RED':
return 'red'
case 'GREEN':
return 'green'
case 'BLUE':
return 'blue'
case 'PURPLE':
return 'purple'
case 'RESET':
return initialState
default:
return state
}
}
const Test02 = () => {
const [color, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1 style={{ color : color }}>색 : { color }</h1>
<p>
<button onClick={ () => dispatch({ type: 'RED' })}>빨강</button>
<button onClick={ () => dispatch({ type: 'GREEN' })}>초록</button>
<button onClick={ () => dispatch({ type: 'BLUE' })}>파랑</button>
<button onClick={ () => dispatch({ type: 'PURPLE' })}>보라</button>
<button onClick={ () => dispatch({ type: 'RESET' })}>초기화</button>
</p>
</div>
);
};
export default Test02;
const reducer = (state, action) => {
switch(action.type){
case 'RED':
return 'red'
case 'GREEN':
return 'green'
case 'BLUE':
return 'blue'
case 'PURPLE':
return 'purple'
case 'RESET':
return initialState
default:
return state
}
}
위에서 보면 그냥 색만 바뀌지 똑같은 작업을 하고있으므로 그걸 한 번 바꿔보자 !!
Test03.jsx
일단은 Test02.jsx 복사해서 붙여넣기
import React from 'react';
const Test03 = () => {
const [color, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1 style={{ color : color }}>색 : { color }</h1>
<p>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'red' })}>빨강</button>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'green' })}>초록</button>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'blue' })}>파랑</button>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'purple' })}>보라</button>
<button onClick={ () => dispatch({ type: 'RESET'})}>초기화</button>
</p>
</div>
);
};
export default Test03;
함수를 CHANGE_COLOR 하나로 통일시키고
어떤 색인지를 구분해야하므로 text도 넘겨준다.
const initialState = { color: 'pink' };
초기화할 때 color 변수다 해서 pink로 잡아준다.
const reducer = (state, action) => {
switch(action.type){
case 'CHANGE_COLOR':
return { color: action.text }
case 'RESET':
return initialState
default:
return state
}
}
return할 때 color 변수다 해서 반환하게 된다.
그러므로
const [state, dispatch] = useReducer(reducer, initialState);
<h1 style={{ color : state.color }}>색 : { state.color }</h1>
여기서 state.color다 해서 찍어줘야하는 거 확인 !!
전체코드
import React from 'react';
import { useReducer } from 'react';
const initialState = { color: 'pink' };
const reducer = (state, action) => {
switch(action.type){
case 'CHANGE_COLOR':
return { color: action.text }
case 'RESET':
return initialState
default:
return state
}
}
const Test03 = () => {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<div>
<h1 style={{ color : state.color }}>색 : { state.color }</h1>
<p>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'red' })}>빨강</button>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'green' })}>초록</button>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'blue' })}>파랑</button>
<button onClick={ () => dispatch({ type: 'CHANGE_COLOR', text: 'purple' })}>보라</button>
<button onClick={ () => dispatch({ type: 'RESET'})}>초기화</button>
</p>
</div>
);
};
export default Test03;
Test04.jsx
https://jsonplaceholder.typicode.com/
3을 입력하면 하나의 json 객체만 받아와진다.
그러므로 우리는 dto로 받으면 된다 !!
axios 설치
그리고 다시 npm start
전에 했던 거 거의 복붙과정
useEffect 함수
useEffect(() => {
const url = 'https://jsonplaceholder.typicode.com/posts/3';
axios.get(url)
.then(res => dispatch({ type: 'SUCCESS', payload: res.data }))
.catch(error => dispatch({ type: 'ERROR' }));
}, []);
상태변수
const [state, dispatch] = useReducer(reducer, initialState);
initialState
초기화
const initialState = {
dto: {},
error: null,
loading: true
}; //객체가 넘어오기 때문에 이렇게 설정
성공일 때는 payload가 res.data를 받아오는 거 확인 !
const reducer = (state, action) => {
switch(action.type){
case 'SUCCESS':
return {
dto: action.payload,
error: null,
loading: false
}
case 'ERROR':
return {
dto:{},
error: '404에러, 데이터를 가져오지 못했습니다.',
loading: false
}
default:
return state
}
이제 return돼서 들어온 값을 뿌려줘야한다!
return (
<div>
<h2>
{
state.dto && state.loading ? '로딩 중' : state.dto.title
}
</h2>
<p>
{
state.error ? state.error : null
}
</p>
</div>
);
- 로딩 중일 때: state.loading이 true이면 '로딩 중'이 표시
- 데이터 로딩이 완료되었을 때: state.loading이 false로 변경되고, state.dto.title에 값이 있으면 그 제목이 표시
에러뜨게 하려면 주소 하나 틀려보면
에러가 뜬다.
로딩중은 새로고침할 때 살짝 떴다가 사라진다.
처음 초기화할 때만 loading값이 true이기 때문 !
Test05.jsx
아이디를 입력을 통해서 데이터 값을 가져오게 할 것이다 !!
이번엔 photo를 이용해보기
import axios from 'axios';
import React from 'react';
import { useEffect } from 'react';
import { useReducer } from 'react';
import { useState } from 'react';
const initialState = {
dto: {},
error: null,
loading: true
};
const reducer = (state, action) => {
switch(action.type){
case 'SUCCESS':
return {
dto: action.payload,
error: null,
loading: false
}
case 'ERROR':
return {
dto:{},
error: '404에러, 데이터를 가져오지 못했습니다.',
loading: false
}
default:
return state
}
}
const Test05 = () => {
const [id, setId] = useState('');
const [search, setSearch] = useState(1);
const [state, dispatch] = useReducer(reducer, initialState);
const onSearch = (id) => {
setSearch(id);
}
useEffect(() => {
const url = `https://jsonplaceholder.typicode.com/photos/${search}`;
axios.get(url)
.then(res => dispatch({ type: 'SUCCESS', payload: res.data }))
.catch(error => dispatch({ type: 'ERROR' }));
}, [search]);
return (
<div>
<h3>
ID 입력 (1 ~ 5000) : <input type='text' value={ id } onChange={ e => setId(e.target.value) } />
<button onClick={ () => onSearch(id) }>검색</button>
</h3>
<br/>
<h2>
{
state.dto && state.loading ? '로딩 중' : state.dto.title
}
<br/>
{
state.loading || <img src={ state.dto.url } alt={ state.dto.id } width={ 100 } height={ 100 }/>
}
</h2>
<p>
{
state.error ? state.error : null
}
</p>
</div>
);
};
export default Test05;
React-Router
1. 리액트는 SPA (Single Page Application) 방식이다.
일반적으로 클라이언트가 요청을 하면 서버에서 요청한 페이지를 보여준다.
그러면서 로딩되는 모습이 보인다.
하지만 리액트는 모든 문서를 다 읽어드린 다음 클라이언트가 요청을 하면
로딩을 하지 않고 바로 보여주기 때문에 속도가 빠르다.
- 기존 웹 페이지처럼 여러 개의 페이지를 사용하며 새로운 페이지를 로드 하는 기존의 MPA 방식이 아니다.
- 새로운 페이지를 로드하지 않고 하나의 페이지 안에서 필요한 데이터만 가져오는 형태를 가진다.
2. 사용자가 입력한 주소를 감지하는 역할을 하며(로그인 요청 / 회원가입 요청인지 감지)
여러 환경에서 동작할 수 있도록 여러 종류의 라우터 컴포넌트를 제공한다.
이중 가장 많이 사용하는 라우터 컴포넌트는 BrowserRouter와 HashRouter이다.
[ 내가 찾아서 추가한 설명 ]
React Router는 React 애플리케이션에서 URL 경로를 관리하고, 컴포넌트를 해당 경로에 맞게 렌더링할 수 있도록 해주는 라이브러리이다.
주로 SPA(Single Page Application)에서 사용되며, 페이지 간의 전환을 매끄럽게 만들어준다.
1. BrowserRouter
- BrowserRouter는 React Router의 기본 라우터로, HTML5의 History API를 사용하여 브라우저의 주소 표시줄을 관리한다.
- 사용자가 주소를 변경할 때 페이지를 새로 고침하지 않고도 URL에 따라 다른 컴포넌트를 표시할 수 있게 해준다.
2. Routes
- Routes는 라우트(route)를 정의하는 컨테이너이다. 여러 개의 Route를 포함할 수 있으며, 현재 URL 경로와 일치하는 첫 번째 Route를 찾아 해당 컴포넌트를 렌더링한다.
import { Routes, Route } from 'react-router-dom';
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
3. Route
- Route는 특정 경로와 연결된 컴포넌트를 정의한다. path 속성에는 URL 경로를 설정하고, element 속성에는 해당 경로에 렌더링할 컴포넌트를 설정한다.
- 예를 들어, / 경로에 Home 컴포넌트를 렌더링하도록 설정할 수 있다.
<Route path="/" element={<Home />} />
<Routes>
<Route path="/" element={<Home />} />
<Route path="/productList" element={<ProductList />} />
<Route path="/productDetail/:id" element={<ProductDetail />} />
</Routes>
- 여기서 :id는 URL 파라미터로, 동적인 값을 받아올 수 있다. 예를 들어, /productDetail/1로 접근하면 1이 ProductDetail 컴포넌트에 전달된다.
4. Link
- Link 컴포넌트는 사용자에게 URL로 이동할 수 있는 링크를 제공한다. 일반 HTML의 <a> 태그와 비슷하지만, 페이지를 새로 고침하지 않고도 다른 컴포넌트로 이동할 수 있다.
- to 속성에는 이동할 경로를 설정다.
import { Link } from 'react-router-dom';
<Link to="/productList">Product List</Link>
요약
- BrowserRouter: 전체 애플리케이션을 감싸는 라우터
- Routes: URL에 맞는 컴포넌트를 렌더링하는 컨테이너
- Route: 특정 경로와 연결된 컴포넌트를 정의
- Link: 사용자에게 다른 페이지로 이동할 수 있는 링크를 제공
[설치]
npm install react-router-dom
yarn add react-router-dom
(둘 중 하나로 !!)
예전에 공부한 사람이 있다면 이렇게 바뀌었다 !
# react-router-dom 변경사항 (2021. 11. 25 기준)
1. Route 컴포넌트를 이제는 Routes 컴포넌트로 필히 감싸주어야 한다.
2. Route 컴포넌트의 매개변수 compent 가 element 로 바뀌었다.
3. useHistory 사라짐 -> useNavigate 함수
4. history.push('/') -> navigate('/')
이제부터는 a태그를 쓰면 안 된다. link 태그로 !!
Route : 어떤 경로로 들어왔을 때 어떤 컴포넌트를 보여 주겠다
Link : Router의 주소를 바꿈, a 태그지만 새로 고침이 안 된다.
day07
src
page01
About.jsx
Ceo.jsx
Home.jsx
Sub01.jsx
NotFiles.jsx
App.js
App01.jsx
npx create-react-app day07
cd day07
npm install react-router-dom
npm start
index.html(화면에 보이는 파일)
| Index.js
App.js
App01.jsx App02.jsx App03.jsx
page01
- About.jsx
- Home.jsx
이런 모양으로 할 것이다 !!
About.jsx
import React from 'react';
const Home = () => {
return (
<div>
<h1>Home Page</h1>
</div>
);
};
export default Home;
Ceo.jsx
import React from 'react';
const About = () => {
return (
<div>
<h1>About Page</h1>
</div>
);
};
export default About;
Home.jsx
import React from 'react';
const Ceo = () => {
return (
<div>
<h1>Ceo Page</h1>
</div>
);
};
export default Ceo;
Sub01.jsx
import React from 'react';
const NotFiles = () => {
return (
<div>
<h1>NotFiles Page</h1>
</div>
);
};
export default NotFiles;
NotFiles.jsx
import React from 'react';
const Sub01 = () => {
return (
<div>
<h1>Sub01 Page</h1>
</div>
);
};
export default Sub01;
App01.jsx
Route라는거 연결한다는 말이다.
<Route path='/' element={ <Home/>}></Route>
주소의 값이 마지막이 /로 끝난다면 Home.jsx로 연결하겠다 !!
원래가 항상 여기 뒤에는 / 이게 있다는 거 생각 !
스프링에서 RequestMapping 해서 연결하는 거랑 여기서 path로 연결하는 거랑 똑같은 역할이다 !!
{/* 화면에 보이는 영역 */}
<Routes>
<Route path='/' element={ <Home/>}></Route>
<Route path='/about' element={ <About/>}></Route>
<Route path='/ceo' element={ <Ceo/>}></Route>
<Route path='/sub01' element={ <Sub01/>}></Route>
<Route path='/*' element={ <NotFiles/>}></Route>
</Routes>
이런식으로 전부 연결해주었다 !!
1. Link
- 클릭 시 바로 이동하는 로직 구현 시에 사용 용이
ex) 상품 리스트에서 상세 페이지 이동 시
- react-router-dom 에서 제공하는 Link 컴포넌트는 DOM 에서 a 태그로 변환이 된다.
- a 태그와 Link 차이
a : 외부 프로젝트로 이동하는 경우
Link : 프로젝트 내에서 페이지 전환하는 경우
<>
<nav>
<ul>
{/* <a href='' />를 사용하면 계속 로딩하느라 빙글빙글 돈다. 그러므로 a 태그를 쓰면안된다.*/}
{/* <a href='' />를 대신해서 <Link />를 사용한다.*/}
<li><Link>Home</Link></li>
</ul>
</nav>
</>
<a href='' />를 사용하면 계속 로딩하느라 빙글빙글 돈다. 그러므로 a 태그를 쓰면안된다.
<a href='' />를 대신해서 <Link />를 사용한다.
{/* <a href='' />를 사용하면 계속 로딩하느라 빙글빙글 돈다. 그러므로 a 태그를 쓰면안된다.*/}
{/* <a href='' />를 대신해서 <Link />를 사용한다.*/}
<li><Link to='/'>Home</Link></li>
전체코드
import React from 'react';
import {BrowserRouter, Routes, Route, Link} from 'react-router-dom';
import Home from './page01/Home';
import About from './page01/About';
import Ceo from './page01/Ceo';
import Sub01 from './page01/Sub01';
import NotFiles from './page01/NotFiles';
const App01 = () => {
return (
<div>
<BrowserRouter>
<>
<nav>
<ul>
{/* <a href='' />를 사용하면 계속 로딩하느라 빙글빙글 돈다. 그러므로 a 태그를 쓰면안된다.*/}
{/* <a href='' />를 대신해서 <Link />를 사용한다.*/}
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/ceo'>Ceo</Link></li>
<li><Link to='/sub01'>Sub01</Link></li>
<li><Link to='/*'>NotFiles</Link></li>
</ul>
</nav>
</>
{/* 화면에 보이는 영역 */}
<Routes>
<Route path='/' element={ <Home/>}></Route>
<Route path='/about' element={ <About/>}></Route>
<Route path='/ceo' element={ <Ceo/>}></Route>
<Route path='/sub01' element={ <Sub01/>}></Route>
<Route path='/*' element={ <NotFiles/>}></Route>
</Routes>
</BrowserRouter>
</div>
);
};
export default App01;
src
page02
About.jsx
Ceo.jsx
Home.jsx
Sub01.jsx
NotFiles.jsx (5개는 다 복사)
NavBar.jsx
navData.jsx
App.js
App02.jsx
className 부여하기 !!!
className = 'page'
About.jsx
import React from 'react';
const About = () => {
return (
<div className='page'>
<h1>About Page</h1>
</div>
);
};
export default About;
Ceo.jsx
import React from 'react';
const Ceo = () => {
return (
<div className='page'>
<h1>Ceo Page</h1>
</div>
);
};
export default Ceo;
Home.jsx
import React from 'react';
const Home = () => {
return (
<div className='page'>
<h1>Home Page</h1>
</div>
);
};
export default Home;
Sub01.jsx
import React from 'react';
const Sub01 = () => {
return (
<div className='page'>
<h1>Sub01 Page</h1>
</div>
);
};
export default Sub01;
NotFiles.jsx
import React from 'react';
const NotFiles = () => {
return (
<div className='page'>
<h1>NotFiles Page</h1>
</div>
);
};
export default NotFiles;
navData.jsx
export default [
{ title: 'HOME', path: '/'},
{ title: 'ABOUT', path: '/about'},
{ title: 'CEO', path: '/ceo'},
{ title: 'SUB01', path: '/sub01'},
]
원래는 두 개를 같은 영역에 잡았는데 따로잡아도되므로
이번엔 따로 잡아보겠다 !!
import React from 'react';
import {BrowserRouter, Routes, Route, Link} from 'react-router-dom';
import NavBar from './page02/NavBar';
import Home from './page02/Home';
import About from './page02/About';
import Ceo from './page02/Ceo';
import Sub01 from './page02/Sub01';
import NotFiles from './page02/NotFiles';
import './css/style02.css'
const App02 = () => {
return (
<BrowserRouter>
<>
<NavBar/>
{/* 화면에 보이는 영역 */}
<Routes>
<Route path='/' element={ <Home/>}></Route>
<Route path='/about' element={ <About/>}></Route>
<Route path='/ceo' element={ <Ceo/>}></Route>
<Route path='/sub01' element={ <Sub01/>}></Route>
<Route path='/*' element={ <NotFiles/>}></Route>
</Routes>
</>
</BrowserRouter>
);
};
export default App02;
NavBar.jsx
import React from 'react';
import { useState } from 'react';
const NavBar = () => {
const [nav, isNav] = useState(false);
const onToggle = () => {
isNav(!nav);
}
return (
<div className='navbar'>
<p className='all-menu' onClick={ onToggle }>menu</p>
<nav className={ `nav ${ nav ? 'on' : ''}` }>
</nav>
</div>
);
};
export default NavBar;
이제 data 뿌려주기
import data from './navData';
return (
<div className='navbar'>
<p className='all-menu' onClick={ onToggle }>menu</p>
<nav className={ `nav ${ nav ? 'on' : ''}` }>
<ul>
{
data.map((item, index) => <li key={ index }>
<Link to={ item.path }>{item.title}</Link>
</li>)
}
</ul>
<p className='close' onClick={ () => isNav(false) }></p>
</nav>
</div>
);
NavBar.jsx 전체코드
import React from 'react';
import { useState } from 'react';
import data from './navData';
import { Link } from 'react-router-dom';
const NavBar = () => {
const [nav, isNav] = useState(false);
const onToggle = () => {
isNav(!nav);
}
return (
<div className='navbar'>
<p className='all-menu' onClick={ onToggle }>menu</p>
<nav className={ `nav ${ nav ? 'on' : ''}` }>
<ul>
{
data.map((item, index) => <li key={ index }>
<Link to={ item.path }>{item.title}</Link>
</li>)
}
</ul>
<p className='close' onClick={ () => isNav(false) }></p>
</nav>
</div>
);
};
export default NavBar;
<nav className={ `nav ${ nav ? 'on' : ''}` }>
내가 궁금했던거는 여기서는 이렇게 적혀있는데 우리가 적용하려는 css 파일에서는
.navbar .nav.on {left: 0;}
.(점) 이 필요한데 괜찮냐? 였다.
결론은
- HTML: 점(.) 없이 클래스 이름만 작성
- CSS: 클래스 선택자 앞에 점(.)을 붙여 사용
흐름을 한 번 더 정리해보면 !!
const [nav, isNav] = useState(false);
상태변수가 false로 되어있는 상태에서
<p className='all-menu' onClick={ onToggle }>menu</p>
menu 글씨를 누르면 onToggle함수에 의해서
const onToggle = () => {
isNav(!nav);
}
true가 되고 !
<nav className={ `nav ${ nav ? 'on' : ''}` }>
- nav 상태가 true일 경우:
- 클래스는 nav on이 되어 .nav.on 스타일이 적용
- 이 상태에서 CSS 파일에 정의된 .nav.on 스타일(navbar .nav.on {left: 0;})이 적용되면서 네비게이션 바가 화면에 표시
- nav 상태가 false일 경우:
- 클래스는 nav만 적용되고, on 클래스가 붙지 않는다. 따라서 .nav.on 스타일이 적용되지 않으면서 네비게이션 바가 화면에서 안보인다.
src
page03
About.jsx (복사)
Home.jsx (복사)
Front.jsx
Profile.jsx
NavBar.jsx
navData.jsx
App.js
App03.jsx
About.jsx
import React from 'react';
const About = () => {
return (
<div>
<h1>About Page</h1>
</div>
);
};
export default About;
Home.jsx
import React from 'react';
const Home = () => {
return (
<div>
<h1>Home Page</h1>
</div>
);
};
export default Home;
Front.jsx
import React from 'react';
const Front = () => {
return (
<div>
<h1>Front Page</h1>
</div>
);
};
export default Front;
Profile.jsx
import React from 'react';
const Profile = () => {
return (
<div>
<h1>Profile Page</h1>
</div>
);
};
export default Profile;
App03.jsx
import React from 'react';
import {BrowserRouter, Routes, Route, Link} from 'react-router-dom';
import Home from './page03/Home';
import About from './page03/About';
import Profile from './page03/Profile';
const App03 = () => {
return (
<BrowserRouter>
<>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/profile'>Profile</Link></li>
</ul>
</>
{/* 화면에 보이는 영역 */}
<Routes>
<Route path='/' element={ <Home/> }/>
<Route path='/about' element={ <About/> }/>
<Route path='/profile' element={ <Profile/> }/>
</Routes>
</BrowserRouter>
);
};
export default App03;
<li><Link to='/front/html'>HTML</Link></li>
<li><Link to='/front/css'>CSS</Link></li>
<li><Link to='/front/javascript'>JavaScript</Link></li>
<li><Link to='/front/react'>React</Link></li>
<li><Link to='/front/vue'>Vue</Link></li>
추가하기
<>
<ul>
<li><Link to='/'>Home</Link></li>
<li><Link to='/about'>About</Link></li>
<li><Link to='/profile'>Profile</Link></li>
<li><Link to='/front/html'>HTML</Link></li>
<li><Link to='/front/css'>CSS</Link></li>
<li><Link to='/front/javascript'>JavaScript</Link></li>
<li><Link to='/front/react'>React</Link></li>
<li><Link to='/front/vue'>Vue</Link></li>
</ul>
</>
<Route path='/front/:변수명' />
<Route path='/front/:name' element={ <Front data={ data }/> }/>
Front.jsx로 가는데 data를 실어가야한다 !!
바깥에 data 상태변수 선언했다 ! (json 객체로 !)
const data = [
{ title: 'html', info: 'HTML입니다.'},
{ title: 'css', info: 'CSS입니다.'},
{ title: 'javascript', info: 'JavaScript입니다.'},
{ title: 'react', info: 'React입니다.'},
{ title: 'vue', info: 'Vue입니다.'},
]
const App03 = () => {
Front.jsx
const Front = ({ data }) => {
위에서 보낸 data 값 받아야한다.
:style 를 route path에 사용하면 useParams() 로 불러와 사용할 수 있다.
: 뒤에 나오는 부분이 params의 key 부분이 되어 :name 는 name가 key가 되어 불러오고 있다.
const { name } = useParams();
<Route path='/front/:name' element={ <Front data={ data }/> }/>
위에서 보낸 name도 받아야한다.
import React from 'react';
import { useParams } from 'react-router-dom';
const Front = ({ data }) => {
const { name } = useParams();
return (
<div>
<h1>Front Page</h1>
<h1>{ name }</h1>
</div>
);
};
export default Front;
둘이 같은지 체크를 해서 filter 처리한다음 map으로 돌려준다.
import React from 'react';
import { useParams } from 'react-router-dom';
const Front = ({ data }) => {
const { name } = useParams();
return (
<div>
<h1>Front Page</h1>
<h1>{ name }</h1>
<hr/>
{
data.filter(item => item.title === name)
.map((item, index) => <div key={ index }>
<h2>
{ item.title } : { item.info }
</h2>
</div>)
}
</div>
);
};
export default Front;
JSX(JavaScript XML)는 React 애플리케이션에서 사용되는 JavaScript의 확장 문법으로, UI를 표현하기 위한 도구이다.
JSX를 사용하면 JavaScript 코드 내에서 HTML과 비슷한 구문을 사용하여 UI를 선언할 수 있다.
.map() 메서드는 배열의 각 요소를 JSX로 변환하여 새로운 배열을 생성한다.
.map() 메서드를 사용하여 데이터 배열을 JSX로 변환하고, 이를 React 컴포넌트에서 렌더링하면 화면에 원하는 결과를 표시할 수 있다.
.filter() 메서드만 사용하면 원본 배열을 필터링하여 조건을 만족하는 요소들을 포함하는 새로운 배열을 반환하지만,
이 배열은 JSX로 렌더링할 준비가 되어있지 않다.
JSX를 생성하려면 .map() 메서드나 다른 순회 메서드를 사용하여 각 요소를 JSX로 변환해주어야 한다.
따라서 원본 데이터를 필터링해서 새로운 배열을 만든다고 해서 그것만으로는 웹 페이지나 UI에 결과를 표시할 수 없다.
JSX로 렌더링하려면 .map()이나 유사한 메서드를 사용하여 각 요소를 JSX로 변환해주어야 한다.
src
page04
Main.jsx
Member.jsx
MemberDetail.jsx
App.js
App04.jsx
10명의 데이터가 들어가있다.
npm install axios 하기 !!
Main.jsx
useEffect를 이용해서 데이터 다 끌고오자 ~!
import axios from 'axios';
import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
const Main = () => {
const [list, setList] = useState([]);
useEffect(() => {
const url = 'https://koreanjson.com/users';
axios.get(url)
.then(res => setList(res.data))
}, [])
return (
<div>
<h1>Main Page / 회원수 : { list.length }</h1>
{
list.map(item => (
<div key={item.id}>
{item.id} {item.name}
</div>
))
}
</div>
);
};
export default Main;
App04.jsx
일단 뜨는거 보기위해서 여기서 <Main/> 부르기
import React from 'react';
import Main from './page04/Main';
const App04 = () => {
return (
<div>
<Main/>
</div>
);
};
export default App04;
App04.jsx
import React from 'react';
import Main from './page04/Main';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
const App04 = () => {
return (
<div>
<BrowserRouter>
<>
{/* 화면에 보이는 영역 */}
<Routes>
<Route path='/' element={ <Main/> }></Route>
</Routes>
</>
</BrowserRouter>
</div>
);
};
export default App04;
Main.jsx
여기서 이제 Member.jsx에 item이다 해서 한 사람 분량 가져가도록 코드를 바꾼다 !!
한 사람 한 사람 데이터를 컴포넌트로 잡은거
return (
<div>
<h1>Main Page / 회원수 : { list.length }</h1>
{
list.map(item => <Member key={ item.id } item={ item }/>)
}
</div>
);
Member.jsx
딱 한 사람 분량을 끌고와서 체크를 해줄 것이다.
import React from 'react';
const Member = ({ item }) => {
const { id, name, email } = item;
const css = {
border: '1px solid pink',
margin: 20,
padding: '20px'
}
return (
<div style={ css }>
<p>아이디 : { id }</p>
<h4>이름 : { name }</h4>
<h4>이메일 : { email }</h4>
</div>
);
};
export default Member;
하나는 링크 / 하나는 버튼을 잡아보자
<p><Link>상세보기</Link></p>
<button>상세보기</button>
상세보기 누를 때 id를 가져가야한다.
<p><Link to={`/member/${id}`}>상세보기</Link></p>
to 보냈으니 path로 받는 부분이 있어야한다 !!
거기가 App04.jsx이다.
<Route path='/member/:memberId' /> 이렇게 해도되지만
<Route path='/member/'>
<Route path=':memberId' element={ <MemberDetail/> }/>
</Route>
이렇게 나눠서 잡아줘도 된다. (나중에 여러값 받을 거 생각해서 이 방법도 알아두기 !!)
import React from 'react';
import Main from './page04/Main';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import MemberDetail from './page04/MemberDetail';
const App04 = () => {
return (
<div>
<BrowserRouter>
<>
{/* 화면에 보이는 영역 */}
<Routes>
<Route path='/' element={ <Main/> }></Route>
<Route path='/member/'>
<Route path=':memberId' element={ <MemberDetail/> }/>
</Route>
</Routes>
</>
</BrowserRouter>
</div>
);
};
export default App04;
MemberDetail.jsx
보낸걸 Param으로 받아줘야한다.
import axios from 'axios';
import React from 'react';
import { useEffect } from 'react';
import { useState } from 'react';
import { useParams } from 'react-router-dom';
const MemberDetail = () => {
const { memberId } = useParams();
const [memberDTO, setMemberDTO] = useState({});
const {name, email, phone, website} = memberDTO;
const css = {
border: '1px solid pink',
margin: 20,
padding: '20px'
}
useEffect(() => {
axios.get(`https://koreanjson.com/users/${memberId}`)
.then(res => setMemberDTO(res.data))
}, []);
return (
<div style={ css }>
<h2>MemberDetail Page : { memberId }</h2>
<h4>이름 : { name }</h4>
<ul>
<li>이메일 : { email }</li>
<li>핸드폰 : { phone }</li>
<li>웹사이트 : { website }</li>
</ul>
</div>
);
};
export default MemberDetail;
뒤로가기 버튼 만들기
2. useNavigate
- useNavigate 훅을 실행하면 페이지 이동을 할 수 있게 해주는 함수를 반환한다.
반환하는 함수를 navigate라는 변수에 저장 후 navigate의 인자로 설정한 path 값을 넘겨주면
해당 경로로 이동할 수 있다.
- 페이지 전환 시 추가로 처리해야 하는 로직이 있으면 useNavigate 사용
ex) 로그인 버튼 클릭 시
회원가입 되어 있는 사용자 -> Main 페이지로 이동
회원가입이 되어 있지 않은 사용자 -> SignUp 페이지로 이동
const navigaete = useNavigate();
const onBack = () => {
//navigaete(-1);
navigaete('/');
}
<button onClick={ onBack }>뒤로</button>
Member.jsx
import { Link, useNavigate } from 'react-router-dom';
const navigaete = useNavigate();
const onGo = () => {
navigaete(`/member/${id}`)
}
<p><Link to={`/member/${id}`}>상세보기</Link></p>
<button onClick={ onGo }>상세보기</button>
'REACT' 카테고리의 다른 글
DAY 79 - Spring + React +MyBatis(MySQL) (2024.10.29) (0) | 2024.10.30 |
---|---|
DAY 78 - React HOMEWORK (2024.10.28) (0) | 2024.10.28 |
DAY 77 - React - 데이터 읽기, 쓰기 / 비동기통신(aixos) / useMemo (2024.10.25) (0) | 2024.10.27 |
DAY 76 - React HOMEWORK (2024.10.24) (1) | 2024.10.24 |
DAY 76 - React - Hook: useEffect / 컴포넌트별 CSS (2024.10.24) (3) | 2024.10.24 |