업데이트 날짜를 찾아볼려고 했는데 한 2021년 11월쯤 업데이트한 것 같다. 거의 다 최신 버전으로 쓰고 있고 배우기도 그렇게 배워서 정리해야된다는 생각을 못했는데, 여러 모듈을 쓰다보니 이전 버전들을 쓰는 블로그들로 인해 헷갈려서 정리하게 되었다.
업데이트를 하면서 번들 크기가 최적화가 가능해졌다. 70%정도로 줄어든다.
React v16.8
React Router v6은 React Hook을 많이 사용하므로 React Router v6으로 업그레이드를 시도하기 전에 React 16.8 이상으로 설치가 되어 있어야 사용이 가능하다. React Router v5는 React >= 15와 호환된다. 내가 글을 쓰고 있는 시점은 react-router-dom은 v6.3.0이다.
설치
npm i react-router-dom
위와 같은 코드를 치면 최신 버전으로 설치가 된다. 이를 package.json에서 확인 가능하다.
달라진 점
1. switch에서 routes로 변경
// v5
import { BrowserRouter, Route, Switch } from "react-router-dom";
import Home from "./pages/Home";
import Write from "./pages/Write";
function App() {
return (
<BrowserRouter>
<Switch>
<Route path="/" component={() => <Home />} />
<Route exact path="/write" component={() => <Write />} />
<Route component={() => <div>Page Not Found</div>} />
</Switch>
</BrowserRouter>
);
}
export default App;
// v6
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import { Main, Page1, Page2, NotFound } from "../pages";
import { Header } from ".";
const Router = () => {
return (
<BrowserRouter>
<Header />
<Routes>
<Route path="/" element={<Main />} />
<Route path="/page1/*" element={<Page1 />} />
<Route path="/page2/*" element={<Page2 />} />
<Route path="/*" element={<NotFound />} />
</Routes>
</BrowserRouter>
);
};
export default Router;
→ v5와 v6을 비교해보았을 때 달라진 점을 서술해보겠다.
- Switch에서 Routes로 이름이 변경되었다.
- Route를 쓰면 exact 옵션이 있었는데 지금은 사라졌다. 이는 복수의 라우팅을 막기 위해서 사용했는데 지금은 복수의 라우팅을 할려면 *URL 뒤에 을 사용한다.
- component에서 element으로 변경되었다. 이 것으로 component를 전달한다. (render도 삭제되었다.)
- path를 상대경로로 하여 지정한다.
2. 중첩 라우팅
// App.js
import React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Web from "../Pages/Web";
import WebPost from "../Pages/WebPost";
const Router = () => {
return (
<BrowserRouter>
<Routes>
<Route path="web/*" element={<Web />}>
<Route path=":id" element={<WebPost />} />
</Route>
</Routes>
</BrowserRouter>
);
};
export default Router;
// Web.js
import React from "react";
import { Link, Routes, Route, Outlet } from "react-router-dom";
import WebPost from "./WebPost";
const Web = () => {
return (
<div>
<h1>This is Web</h1>
<ul>
<li>
<Link to="1">Post #1</Link>
</li>
<li>
<Link to="2">Post #2</Link>
</li>
<li>
<Link to="3">Post #3</Link>
</li>
<li>
<Link to="4">Post #4</Link>
</li>
</ul>
<Outlet />
</div>
);
};
export default Web;
// WebPost.js
import React from "react";
const WebPost = () => {
return <div>This is 포스트</div>;
};
export default WebPost;
→ 중첩라우팅을 쓸려면 App.js에서 url뒤에 와일드 카드를 써준다. 위에서 설명한 것처럼 라우트를 설정하면 된다. 그리고 겹치는 부분은 web.js처럼 link컴포넌트를 주어 다르게 설정하여 클릭시 다른 주소에 들어가게 한다. 그리고 중첩되는 부분은 outlet모듈을 불러와 써준다. 여기서 중첩된 부분은 webpost를 의미한다. 아이디가 달라지면 li안의 글이 달라지고 나머지(webpost.js)는 같은 걸 알 수 있다.
3. props
1) useLocation을 사용하여 components와 결합
const { pathname } = useLocation();
uselocation을 사용하면 pathname과 search를 이용하여 사용할 수 있다.
const Header = () => {
const { pathname } = useLocation();
return (
<HeaderWrapper>
{/* header 태그 */}
<List>
{/* ul 태그 */}
<Item selected={pathname.startsWith("/web")}>
{/* li 태그 */}
<Link to="/web">Go to Web</Link>
</Item>
<Item selected={pathname === "/design"}>
<Link to="/design">Go to Design</Link>
</Item>
<Item selected={pathname === "/server"}>
<Link to="/server">Go to Server</Link>
</Item>
</List>
</HeaderWrapper>
);
};
export default Header;
위와 같이 사용이 가능하다.
2) useParams()
import React from "react";
import { useParams } from "react-router";
const WebPost = () => {
// 현재 주소의 값이 <http://localhost:3000/home/3> 일때
// id는 3이 된다.
const { id } = useParams();
return <div>#{id}번째 포스트</div>;
};
export default WebPost;
- index props
<Route index element={<Home />} />
이 props 는 path="/"와 동일한 의미를 가진다.
4. useRoutes
기존의 react-router-config가 useRoutes라는 Hook으로 변경되었다. 패키지를 추가로 설치해야했던 것과는 달리 useRoutes라는 훅으로 routes를 구성할 수 있게 되었다.
function App() {
let element = useRoutes([
// Route에서 사용하는 props의 요소들과 동일
{ path: "/", element: <Home /> },
{ path: "dashboard", element: <Dashboard /> },
{
path: "invoices",
element: <Invoices />,
// 중첩 라우트의 경우도 Route에서와 같이 children이라는 property를 사용
children: [
{ path: ":id", element: <Invoice /> },
{ path: "sent", element: <SentInvoices /> }
]
},
// NotFound 페이지는 다음과 같이 구현할 수 있음
{ path: "*", element: <NotFound /> }
]);
// element를 return함으로써 적절한 계층으로 구성된 element가 렌더링 될 수 있도록 함
return element;
}
위에 쓰던 중첩 라우팅과 비슷하다는 생각이 든다. useRoutes보다 위에 있는 중첩 라우팅을 더 많이 사용할 것 같다.
또한, 공식문서에 따르면 <Routes>는 useRoutes를 감싼 wrapper라고 설명한다. <Routes>와 useRoutes 모두를 권장하며, 둘 중 자신이 더 선호하는 것을 사용하면 된다.
5. useNavigate
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
개인적으로 위에 링크한 사이트에 들어가보면 useNavigate와 useLocation을 같이 쓰는데 이런 식으로 많이 사용한다.
- 많이 사용하는 문법
→ navigate로 가져와서 location으로 사용한다.// edit.js import { useLocation } from "react-router"; const Edit = () => { const { state } = useLocation(); console.log(state); }
- // home.js import { useNavigate } from 'react-router'; const handleClick = (e) => { const navigate = useNavigate(); navigate('/edit', { state: e.target.value }); }
navigate(to, { state: any});
// navigate("../success", { replace: true});
위의 코드는 navigate의 문법이다. 여기서 to는 react의 Link를 생각하면 편하다. 내가 가고자하는 링크를 적어주면 된다. state는 다음 페이지에 넘겨주고자 하는 값을 쓰면 된다. 그리고 replace는 페이지를 이동시 현재 페이지가 페이지 기록에 남지 않는다.
만약 이전 페이지가 about이라면 replace:true가 되어 있기에 about페이지보다 전의 페이지로 이동하게 된다.
import { useNavigate } from "react-router-dom";
function App() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-2)}>
Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
Go forward
</button>
<button onClick={() => navigate(2)}>
Go 2 pages forward
</button>
</>
);
}
이전에는 navigate대신 history를 사용했는데 이 기능을 대체하여 navigate도 history의 기능을 사용가능하다.
이러한 변화의 가장 주된 이유는 React suspense와의 호환성을 더 높이기 위함이다. 아직 이전의 클릭이 로딩 중인 상태에서 다른 라우트로의 링크를 클릭한 경우와 같이 pending이 충돌되는 경우에 더 부드러운 경험(smoother experience)를 제공할 수 있다. navigate API는 이전의 pending 작업을 알아차리고 해당 내용을 history stack에 PUSH하는 것이 아니라 REPLACE함으로써 로드되지 않은 기록으로 끝나지 않도록 한다. 이 글로 보아 react-router-dom은 더욱 사용자에게 유용한 사용 경험을 주기 위하여 업데이트를 한 것을 알 수 있다.