https://react.vlpt.us/react-router/
위 글의 리액트 라우터 사용 과정을 따라가보려 했으나, 해당 글이 react-router-dom v5 기준으로 작성되어 현재 버전인 v6에서 쓰려고 보니 제대로 동작하지 않았다.
그래서 react-router-dom v6에서 업데이트된 Hooks 를 사용해 이전과 달라진 점을 체크하며 과정을 따라해보았다.
* 과정을 따라하는 데에 체크가 필요한 사항들을 위주로 정리했으므로, 모든 변경점을 알고 싶다면 밑의 글을 참고하면 된다.
참고한 글: https://velog.io/@soryeongk/ReactRouterDomV6
react router v6 공식 문서: https://reactrouter.com/docs/en/v6
cra 및 cd해서 경로 이동 후 react-router-dom 설치
$ yarn add react-router-dom
라우터 적용 :: index.js에서 BrowserRouter 라는 컴포넌트를 사용해야 라우터 사용 가능
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
Route로 특정 주소에 컴포넌트를 연결하는 방식 비교
v5
import { Switch, Route } from 'react-router-dom';
...
<Switch>
<Route path="/" exact={true} component={Home}>
<Route render={({location}) => (<div>
<h2>이 페이지는 존재하지 않습니다: {location.pathname}</h2>
</div> )}
/>
</Switch>
- exact를 쓰면 해당 경로와 완전히 똑같을 때만 컴포넌트가 보이고, 쓰지 않으면 경로에 /가 포함된 경우 항상 이 컴포넌트가 함께 보였다.
- 여러 Route를 감쌀 때 Switch를 사용해서 규칙이 일치하는 라우트 하나만 렌더링 시켜줄 수 있었다.
- path를 따로 지정해주지 않으면 아무것도 일치하지 않을 때 보여줄 Not Found 페이지도 구현 가능했다.
v6
import { Route, Routes, useLocation } from 'react-router-dom';
const location = useLocation();
return (
...
<Routes>
<Route path="/" element={<Home />} />
<Route path="/*" element={<div>
<h2>이 페이지는 존재하지 않습니다: {location.pathname}</h2>
</div>}
/>
</Routes>
...
)
- component 대신 element를 쓰도록 변경됐다. element 내부에 들어가는 것도 컴포넌트를 부르는 형태
- render 도 삭제됐다. element 안에 직접 집어넣으면 됨.
- exact 옵션이 삭제됐다. 여러 라우팅을 매칭하고 싶은 경우 URL 뒤에 * 을 사용한다.
- path를 기존에는 절대경로로 지정했으나, 상대경로로 지정할 수 있게 됐다.
파라미터
v5
const Profile = ({ match }) => {
const { username } = match.params;
const profile = profileData[username];
return (
<div>
<h3>
{username}({profile.name})
</h3>
<p>{profile.description}</p>
</div>
);
};
- match 객체 안의 params 값을 사용했다. 더 정확히 말하자면, App 쪽 Route path에서 "/profiles/:username"의 :username을 넣어준 params 값을 match props로 전달받아 사용했다.
v6
import { useParams } from 'react-router-dom';
const { username } = useParams();
const profile = profileData[username];
- match에 접근할 필요없이 useParams를 사용해 params를 바로 불러올 수 있다.
쿼리
v5 : qs 라이브러리를 깔아서 사용했다.
v6 : react-router-dom 라이브러리의 useSearchParams 사용 가능
import { useSearchParams } from 'react-router-dom';
const [searchParams] = useSearchParams();
const detail = searchParams.get('detail') === 'true';
서브 라우트(중첩 라우트)
라우트 내부의 라우트를 만드는 것. v6 에서는 2가지 방법이 있다.
1. 상위 라우터에서 중첩 라우터를 사용하고, 중첩 라우터에서 Outlet 컴포넌트를 사용한다.
2. 중첩 라우터에 곧바로 기재한다.
벨로퍼트가 사용한 예제는 2번에 더 가까워 보인다. v5를 v6 방식으로 바꿔보았다.
// Profiles.js
import { NavLink, Route, Routes } from 'react-router-dom';
const activeStyle = {
background: 'black', color: 'white'
}
...
<ul>
<li>
<NavLink
to="/profiles/yejin"
style={{ background: 'blue', color: 'white' }}
>
yejin
</NavLink>
</li>
<li>
<NavLink
to="yeonju"
style={activeStyle}
>
yeonju
</NavLink>
</li>
</ul>
<Routes>
// /profiles 경로일 때만 보이는 JSX
<Route path="/" element={<div>유저를 선택해주세요.</div>} />
// /profiles/... 경로일 때 보이는 컴포넌트
<Route path=":username" element={<Profile />} />
</Routes>
// App.js 라우팅 부분
<Routes>
<Route path="/" element={<Home />} />
<Route path='/*' element={<div>
<h2>이 페이지는 존재하지 않습니다: {location.pathname}</h2>
</div>}
/>
<Route path="/about" element={<About />} />
<Route path="/profiles/*" element={<Profiles />} />
<Route path="/history" element={<HistorySample />} />
</Routes>
- 절대경로가 아닌 상대경로도 사용할 수 있게 되었으므로 상위 라우터에서 정해준 경로 뒷부분만 적으면 된다.
즉, 상위 라우터에서 `/profiles/*` 로 라우팅해준 컴포넌트에서는 to나 path에 `/profiles/asdf` 을 적는 것과 `asdf`만 적는 것이 같다.
- activeClassName, activeStyle props 제거 - 대신 style과 className에 함수를 전달할 수 있게 됐다.
- <NavLink exact> 는 <NavLink end>로 변경되었다고 한다.
- <Link>의 component prop이 제거되어 더이상 지원하지 않는다.
history, location, match props 제거 - Hooks 사용
v5 | v6 |
history 또는 useHistory | useNavigate |
location | useLocation |
match.params | useParams |
* v5 에서는 라우트로 사용된 컴포넌트에 해당 객체들이 props로 들어왔지만, v6 에서는 Hooks로 사용할 수 있게 되었다.
* useParams 는 url에서 받아오는 string을, useLocation은 객체를 넘겨주는 듯 하다.
v5 - history 객체
function HistorySample({ history }) {
const goBack = () => {
history.goBack();
};
const goHome = () => {
history.push('/');
};
useEffect(() => {
console.log(history);
const unblock = history.block('정말 떠나실건가요?');
return () => {
unblock();
};
}, [history]);
...
}
v6 - useNavigate
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();
const goBack = () => {
const confirm = window.confirm('정말 떠나시겠어요?');
if (confirm) {
navigate(-1);
}
};
const goHome = () => {
navigate('/');
};
- history.push 와 history.replace 모두 navigate라는 명칭으로 사용한다. replace 기능은 `navigate(to, {replace, true})`의 형태로 사용 가능하다. state를 사용하면 `navigate(to, {state})` 의 형태로 사용 가능하다. (to에는 경로를 넣으면 된다)
- history나 useHistory에서는 `go, goBack, goForward` 였던 것들이 v6에서는 navigate로 통일되어 index를 넣는 것으로 해결할 수 있다.
* 한꺼번에 보기
v5
import React from 'react';
import { withRouter } from 'react-router-dom';
const WithRouterSample = ({ location, match, history }) => {
return (
<div>
<h4>location</h4>
<textarea value={JSON.stringify(location, null, 2)} readOnly />
<h4>match</h4>
<textarea value={JSON.stringify(match, null, 2)} readOnly />
<button onClick={() => history.push('/')}>홈으로</button>
</div>
);
};
v6
import { useParams, useLocation, useNavigate } from 'react-router-dom';
const WithRouterSample = () => {
const params = useParams(); // url에 대한 정보
const location = useLocation(); // 현재 페이지에 대한 정보
const navigate = useNavigate();
return (
<div>
<h4>params</h4>
<textarea value={JSON.stringify(params)} readOnly />
<h4>location</h4>
<textarea value={JSON.stringify(location, null, 2)} readOnly />
<h4>navigate</h4>
<button onClick={() => navigate(-1)}>홈으로</button>
</div>
)
}
'React' 카테고리의 다른 글
[React 공부] Context API 전역값 관리 & Immer 사용 불변성 관리 (0) | 2022.07.05 |
---|---|
[React 공부] 클래스형 컴포넌트 (0) | 2022.07.05 |
[React 공부] 함수형 컴포넌트의 Hooks (0) | 2022.07.05 |