https://tigger.dev/entry/React-Router를-활용해-원하는-페이지로-이동하기
https://www.daleseo.com/react-router-basic/
https://velog.io/@devstone/react-router-dom-이해하고-활용하기
https://reactrouter.com/docs/en/v6/upgrading/v5
최신 버전인 react-router-dom v6을 기준으로 작성하였다.
SPA는 index.html 파일에 div
하나를 두고 자바스크립트를 이용해서 모든 부분을 컴포넌트화해서 렌더링하는 구조를 취하는데, 라우팅을 구현할 때 React Router를 사용하면 SPA의 장점을 살리면서도 기존 웹사이트의 라우팅을 그대로 구현하는 게 가능하다.
react-router-dom
은 react-router
v4 버전에서 처음 릴리즈 된 라우팅 모듈로, react-router
모듈에 dom이 바인딩 되어 있는 모듈이다. v3버전까진 react-router
모듈 하나만을 사용할 수 있었는데, v4버전 이후 react-router
모듈을 코어로, 웹 개발자를 위한 react-router-dom
과 앱 개발자를 위한 react-router-native
가 릴리즈 되었다. 우리는 웹을 주로 개발할 것이니 react-router-dom
을 중점으로 사용할 것이다.
BrowserRouter
: html5의 history API를 이용해 UI 업데이트를 한다.Routes
: Route
로 생성된 자식컴포넌트 중에 path와 URL이 매칭되는 첫번째 Route의 컴포넌트를 렌더링한다.이전의 Switch가 Routes로 바뀌었다.
Route
: 컴포넌트 별로 원하는 url을 지정한다.Link
: 클릭 시 지정한 URL로 이동하는 링크를 생성한다. 아예 새로운 페이지를 불러오므로 기존 컴포넌트의 상태값은 소멸된다.
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';
import { MainPage } from './pages';
import { Header } from './components';
const NotFound = () => {
return <Link to="..">돌아가!!</Link>;
};
const AboutPage = () => {
return <>about</>;
};
const PicturePage = ({ data }) => {
return (
<>
<h1>data = {data}</h1>
Picture
</>
);
};
const Router = () => {
return (
<>
<BrowserRouter>
<Header />
<Routes>
<Route path="/" element={<MainPage />} />
{/* <Route path="/*" element={<NotFound />} /> */}
<Route path="/about" element={<AboutPage />} />
<Route
path="/picture"
element={<PicturePage data="전달된 props" />}
/>
<Route path="/*" element={<NotFound />} />
</Routes>
</BrowserRouter>
</>
);
};
export default Router;
위의 세 컴포넌트는 SPA 구조에서 전체적인 라우팅 틀을 잡기 위해 사용한다. 라우팅 하고 싶은 컴포넌트들을 <BrowserRouter>
,<Routes>
로 감싸고 <Route>
로 컴포넌트에 해당하는 URL을 지정해준다. 아래의 코드는 간단한 라우팅 컴포넌트인데, * 같은 경우 와일드카드처럼 해당되는 모든 URL에 대해 대응한다. 이 때 순서가 중요한데, 만약 와일드카드로 지정한 <NotFound>
의 라우트가 상단에 배치될 경우, 다른 URL로 라우팅 되야하는 경우에도 <NotFound>
만 라우팅하게 된다. 이는 원래 의도한 동작과는 큰 차이가 있을 것이다.
import * as React from "react";
import { Link } from "react-router-dom";
function UsersIndexPage({ users }) {
return (
<div>
<h1>Users</h1>
<ul>
{users.map(user => (
<li key={user.id}>
<Link to={user.id}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}
특정 주소로 넘어갈 수 있게 해준다. path는 상대 경로로 표현하는데, .
, ..
같은 문법도 사용할 수 있다.
v5까지는 다음 링크처럼 Link에 props를 넣어서 전달할 수 있었으나
https://velog.io/@sham/Router-Props-link로-전달하는-props
v6부터는 더이상 link로 props를 전달하지 못하게 되었다. 커스텀 훅으로 link를 직접 만들어서 props를 전달하는 것을 구현할 수는 있다고 한다.
https://reactrouter.com/docs/en/v6/upgrading/v5#remove-link-component-prop
<Route>
로 설정한 컴포넌트가 받는 props(이젠 안된다!)이전 버전까지는 Route에 할당된 컴포넌트라는 자동으로 match, location, history라는 값이 props으로 전달되었으나, 이제는 더이상 props으로 해당 값들이 전달되지 않는다.
useNavigate(useHistory), useLocation, useMatch 훅으로 조작해야 한다.
이전의 useHistory가 useNavigate로 바뀌었다.
// This is a React Router v5 app
import { useHistory } from "react-router-dom";
function App() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
// This is a React Router v6 app
import { useNavigate } from "react-router-dom";
function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
history.push와 history.replace가 useNavigate()를 할당받는 메서드 하나로 대체되었다.
기존의 v5의 Redirect가 더 나은 사람들을 위해 v6에서도 Navigate
컴포넌트를 이용해서 v5처럼 사용할 수 있다.
import { Navigate } from "react-router-dom";
function App() {
return <Navigate to="/home" replace state={state} />;
}
to는 Link
와 동일하게 이동하고 싶은 url, replace는 history.replace
처럼 스택에 밀어넣는 게 아니라 현재 history를 교체하고 싶을 때, state는 link에서 불가능해진 props을 건네줄 수 있다.
Navigate로 들어간 props는 이동한 컴포넌트에서 useLocation의 state에서 확인할 수 있다.
import { useMatch } from "@reach/router"
const App = () => {
const match = useMatch('/hot/:item');
return match ? (
<div>Hot {match.item}</div>
) : (
<div>Uncool</div>
)
)
컴포넌트가 렌더링된 시점의 url가 인자 안의 url이 동일한지를 체크한다. 동일하다면 정보를 담은 객체가, 동잃지 않다면 null을 리턴한다.
객체
params: {}
pathname: "/pro"
pathnameBase: "/pro"
pattern: {path: "/pro", caseSensitive: false, end: true}
import * as React from 'react';
import { useLocation } from 'react-router-dom';
function App() {
let location = useLocation();
console.log(location);
React.useEffect(() => {
ga('send', 'pageview');
}, [location]);
return (
// ...
);
}
현재의 location 객체를 리턴한다.
객체
hash: ""
key: "crk8wn68"
pathname: "/pro"
search: ""
state: null