React v16.8
React Router v6는 React Hook 을 많이 사용하므로 v5 → v6로 업그레이드 하기 전에 React 16.8 이상 이어야 한다.
1. Routes 의 사용법 변경
1. Switch 컴포넌트 대신 Routes 컴포넌트 사용
- Switch 의 네이밍이 Routes 로 변경
- exact 옵션 삭제
- exact 는 더이상 사용하지 않고 여러 라우팅을 매칭하고 싶은 경우 URL 뒤에 * 을 사용
- component 방식 변경 ( component={ } & render{( ) ⇒ component} 삭제 )
- component props 대신 elemet props 로 component를 전달
- path 를 상대경로로 지정
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| 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;
|
2. 중첩 라우팅
- Router,js 에서 자식 태그로 중첩하는 라우터를 기재하고, Web.js 에서 Outlet 라이브러리를 통해 가져온다.
- exact 를 쓰지 않는 대신 “ /* ” 가 필수 이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
| // Router.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;
|
3. New Naviagtion API
useHistory hook 과 Redirect Component는 이제 새로운 useNavigation hook과 Navigate Component로 대체된다.
3-1. useNavigation hook
- v5에서 imperative하게 이동하기 위해서는 useHistory hook으로 가져온 history 객체의 메서드를 호출하였다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| // react-router v5
import { useHistory } from "react-router-dom";
const App = () => {
const history = useHistory();
const handleClick = () => history.push("/home");
return (
<div>
<button onClick={handleClick}>Go to Home</button>
</div>
);
};
|
- 하지만 v6에서는 useNavigate hook 으로 가져온 navigate 함수를 직접적으로 호출하게 된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| // react-router v6
import { useNavigate } from "react-router-dom";
const App = () => {
const navigate = useNavigate();
const handleClick = () => navigate("/home");
return (
<div>
<button onClick={handleClick}>Go to Home</button>
</div>
);
};
|
- 기존의 history 객체의 go( ), goForward( ), goBack( ) 메서드의 경우 아래와 같이 navigate 함수에 정수 인자를 전달하는 방식으로 대체된다.
1
2
3
4
5
| navigate(N); // history.go(N)
navigate(1); // history.goForward()
navigate(-1); // history.goBack()
navigate(2); // go 2 pages forward
navigate(-2); // go 2 pages backward
|
- navigate 함수에 path 값을 넣을 때, 주소 이동 간 보존해야 할 state가 있다면 두 번째 인자(Optional)에 state 필드로 객체를 전달한다.
1
| navigate("/home", { state });
|
- naviage 함수는 기본적으로 hitory push 방식으로 동작한다. 만약 history replace 방식으로 사용하고 싶다면, 두 번째 인자를 넣을 때 replace: true 필드를 같이 전달한다.
1
| navigate("/home", { state, replace: true });
|
3-2. Navigate Component
- v6에서는 Navigate Component를 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
| // react-router v6
import { Navigate } from "react-router-dom";
const LoginPage = () => {
const user = useLoginContext();
return (
<>
{user && <Navigate to="/home" replace />}
<div>...</div>
</>
);
};
|
- v6의 Navigate Component 는 useNavigate hook 을 래핑한 컴포넌트이기 때문에 받아오는 props 가 useNavigate 의 인자와 동일하기 때문에 Navigate Component도 기본값으로 history push 를 사용하여 이동하므로, history replace 로 이동하기 위해서는 replace props를 넘겨줘야 한다.
1
| <Navigate to="/home" state={state} replace />
|
4. Relative routes & Nested routes
- v5에서는 useRouteMatch 를 통해 현재 경로를 가져온 뒤 path props에 라우트할 경로를 덧붙여 넣어주는 방식으로 경로를 지정해주어야 했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| // react-router v5
const App = () => {
return (
<Switch>
<Route path={'/welcome'}>
<WelcomePage />
</Route>
{/* OR */}
{/* <Route path={'/welcome'} component={WelcomePage} /> */}
</Switch>
)
}
const Welcome = () => {
const match = useRouteMatch()
return (
<>
<p>Welcome Page</p>
<Link to={`${match.path}/new-user`}>New User</Link>
<Switch>
<Route path={`${match.path}/new-user`} component={() => <p>Hi New User!</p>} />
</Switch>
<>
)
}
|
- 하지만 v6부터는 Route 컴포넌트와 Link 컴포넌트가 부모의 라우트 경로를 보고 이로부터 자신의 경로를 빌드하므로, 더 이상 처음부터 경로를 만들 필요가 없다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // react-router v6
const App = () => {
return (
<Routes>
<Route path={'/welcome/*'} element={<WelcomePage />} />
{/*
path={'/welcome'} won't work here, as the new router uses "exact" route matching.
concatenate "/*" to indicate there are more paths to be matched in "WelcomePage".
*/}
</Routes>
)
}
const Welcome = () => {
return (
<>
<p>Welcome Page</p>
<Link to="/new-user">New User</Link>
{/* automatically builds path "/welcome/new-user" */}
<Routes>
<Route path="/new-user" element={<p>Hi New User!</p>} />
</Routes>
<>
)
}
|
- v6에서는 Route 의 자식 컴포넌트로 Route 컴포넌트로 둘 수 있다. 들여쓰기를 통해 가독성을 확보할 수 있고,
<Outlet />
을 통해 하위 라우트 컴포넌트를 그려줄 곳을 명시할 수 있다. Outlet 컴포넌트가 위치한 곳에 render가 된다. - Route Component의 children props는 nested route 기능을 위해 예약이 되어 있기 때문에 element props로 component를 내려준다.
- 이제 단독으로 Route Component를 사용할 수 없으며, Route Compoennt는 무조건 Routes Component로 감싸져 있어야 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // react-router v6
const App = () => {
return (
<Routes>
<Route path={'/welcome'} element={<WelcomePage />}>
// notice the absence of trailing "/*": the router can explicitly see there is a nested route
<Route path={'/new-user'} element={<p>Hi New User!</p>} />
</Route>
</Routes>
)
}
const Welcome = () => {
return (
<>
<p>Welcome Page</p>
<Link to={'/new-user'}>New User</Link>
<Outlet /> <- indicates where the element props of the nested route should render
<>
)
}
|
5. useMatch replaces useRouteMatch
- useMatch 또한 새로 도입된 매치 알고리즘을 사용하기 때문에 무조건 패턴 경로를 인자로 넣어줘야 한다.
1
2
3
4
5
6
7
8
9
10
11
| import { useMatch } from 'react-router-dom'
const match = useMatch('/home')
if (match) {
// current path is "/home"
...
} else {
// current path is NOT "/home"
...
}
|
- 리턴하는 Match 타입의 객체가 변경되어 PathMatch 타입을 리턴한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // react-router v5
interface match<ParamKey> {
isExact: boolean;
params: Params<ParamKey>;
path: string;
url: string;
}
// react-router v6
interface PathMatch<ParamKey> {
params: Params<ParamKey>;
pathname: string;
pattern: PathPattern;
}
interface PathPattern {
path: string;
caseSensitive?: boolean;
end?: boolean;
}
|
6. No Optional params (nested route)
- 더 이상 경로 Parameter 를 Optional 하게 선택하지 못하게 되었다.
- 이 경우 /products/12345 경로는 상위 ProductDetail 라우트에 매치되어 productId:”12345” 파라미터를 들고 ProductDetail Component를 렌더할 것이고, /products 경로는 하위 ProductDetail 라우트에 매치되어 productId: null 파라미터를 들고 ProductDetail Component를 렌더할 것이다.
1
2
3
4
| <Route path="/products">
<Route path=":productId" element={<ProductDetail />} />
<Route path="" element={<ProductDetail />} />
</Route>
|