최근 react-router-dom 버전 6로 업그레이드 되었습니다. 버전 6과 버전 5는 많은 차이가 있기 때문에 그부분에 있어서도 중점적으로 확인해보는 것을 권장드립니다.
$ yarn add react-router-dom@6
index.js
import { BrowserRouter } from "react-router-dom";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<BrowserRouter>
<App />
</BrowserRouter>
);
Next.js
의 경우 pages폴더 안에서 폴더를 어떻게 만드느냐에 따라서 자동으로 ui를 세팅해주기 때문에 미리 연습해본다는 마음으로 관리를 해보는 것도 좋습니다.App.js
import React from "react";
import { Routes, Route } from "react-router-dom";
import Posts from "./Posts";
import Users from "./Users";
function App() {
return (
<div>
<Routes>
<Route path="posts" element={<Posts />} />
<Route path="users" element={<Users />} />
</Routes>
</div>
);
}
export default App;
Route
는 어떤 주소로 접근했을 때 어떤 컴포넌트를 보여주고 싶은지를 정하는 태그입니다.<Route path="접속할 port" element={보여줄 컴포넌트} />
Routes
주소변경을 감지해서 아래에 있는 route중 일치하는 route를 찾고 보여주는 역할을 합니다.<Link to="posts">Posts</Link>
to="" 이동하고 싶은 페이지path="*"
을 쓰는 경우 일치하지 않는 모든 경로에 해당하는 경우 해당 컴포넌트를 보여줍니다.function App() {
return (
<div>
<Link to="posts">Posts</Link> | <Link to="users">Users</Link>
<Routes>
<Route path="posts" element={<Posts />} />
<Route path="users" element={<Users />} />
<Route path="*" element={<p>Not Found</p>} />
</Routes>
</div>
);
}
App.js
<Routes>
<Route path="posts" element={<Posts />}>
<Route path=":id" element={<PostDetail />} />
</Route>
</Routes>
Post.js
outlet
import { Outlet } from "react-router-dom";
function Posts() {
return (
<div>
<Outlet />
</div>
);
}
App.js
<Route path="posts" element={<Posts />}>
<Route index element={<PostIndex />} /> ✅
<Route path=":id" element={<PostDetail />} />
</Route>
Route구조 정리
- Posts페이지 : nav바라던지, 뒤로가기 공통요소를 넣습니다.
- Index페이지 : PostList
- :id : 디테일페이지
PostDetail
import { useParams } from "react-router-dom";
function PostDetail() {
const params = useParams();
return <div>{params.id}</div>;
}
Posts>PostIndex>index.js
function PostIndex() {
return (
<div>
{postData.map((post) => (
<Link to={`/posts/${post.id}`}>
<p>{post.title}</p>
</Link>
))}
</div>
);
}
posts>PostDetail>index.js
useParams로 얻어낸 해당 아이디에 맞는 디테일 정보를 표시합니다.
function PostDetail() {
const params = useParams();
const post = postData.find((post) => post.id === parseInt(params.id));
return (
<>
****
<div>{post.title}</div>
<div>{post.body}</div>
</>
);
}
단, params.id는 문자, post.id는 숫자이기 때문에 타입을 일치 시켜줘야합니다.
function PostIndex() {
const [searchParams, setSearchParams] = useSearchParams();
const [posts, setPosts] = useState(postData);
useEffect(() => {
setPosts(
postData.filter((post) => {
const filter = searchParams.get("filter");
const title = post.title.toLowerCase();
return filter ? title.includes(filter) : true;
})
);
}, []);
return (
<div>
{posts.map((post) => (
<Link to={`/posts/${post.id}`}>
<p>{post.title}</p>
</Link>
))}
</div>
);
}
function PostIndex() {
const [searchParams, setSearchParams] = useSearchParams();
const [posts, setPosts] = useState(postData);
const searchInputHandler = (e) => {
const filter = e.target.value;
filter ? setSearchParams({ filter }) : setSearchParams({});
};
useEffect(() => {
setPosts(
postData.filter((post) => {
const filter = searchParams.get("filter");
const title = post.title.toLowerCase();
return filter ? title.includes(filter) : true;
})
);
}, [searchParams]);
return (
<div>
<input onChange={searchInputHandler} />
{posts.map((post) => (
<Link to={`/posts/${post.id}`}>
<p>{post.title}</p>
</Link>
))}
</div>
);
}
현재 url관련한 위치를 알려줍니다.
아래와 같이 hash
, key
, pathname
(주소), search
, state
(주소와 함께 특정정보를 전달할수 있습니다.) 정보가 들어있습니다.
사실 PostDetail 페이지의 경우 단순 id와 일치하는 정보만 필요하기 때문에 postData를 불러올 필요는 없습니다.
즉, const post = postData.find((post) => post.id === parseInt(params.id));
부분을 PostIndex페이지에서 내려줘도 괜찮습니다.
PostIndex > index.js
return (
<div>
<input onChange={searchInputHandler} />
{posts.map((post) => (
<Link
to={`/posts/${post.id}`}
state={{
post: posts.find((data) => data.id === post.id),
}} ✅
>
<p>{post.title}</p>
</Link>
))}
</div>
);
PostDetail > index.js
import React from "react";
import { useLocation } from "react-router-dom";
function PostDetail() {
const location = useLocation();
//post가 없더라도 에러가 나지 않도록 null처리
const { post } = location.state ? location.state : { post: null };
//post가 없는 경우 not found
if (!post) return <p>Not Found</p>;
return (
<>
<div>title: {post.title}</div>
<div>body: {post.body}</div>
</>
);
}
export default PostDetail;
onClick={() => navigate(-1, {replace:true})}
로 설정하는 경우 기록이 아닌, 상위 루트로 가게 됩니다.import { useLocation, useNavigate } from "react-router-dom";
function PostDetail() {
const location = useLocation();
const navigate = useNavigate(); ✅
const { post } = location.state ? location.state : { post: null };
if (!post) return <p>Not Found!</p>;
return (
<>
<div>title: {post.title}</div>
<div>body: {post.body}</div>
<button onClick={() => navigate("/users", {state: {data:1}})}>유저로 가기</button> ✅
<button onClick={() => navigate(-1)}>뒤로가기</button>
</>
);
}
App.js
function App() {
const navigate = useNavigate();
const location = useLocation();
return (
<div>
<h5>{location.pathname}</h5>
<button onClick={() => navigate(-1)}>뒤로가기</button>
<nav>
<Link to="posts">Posts</Link> | <Link to="users">Users</Link>
</nav>
<Routes>
<Route path="posts" element={<PostIndex />}>
<Route index element={<Posts />} />
<Route path=":id" element={<PostDetail />} />
</Route>
<Route path="users" element={<Users />}>
<Route index element={<UserIndex />} />
<Route path=":id" element={<UserDetail />} />
</Route>
<Route path="*" element={<p>Not Found</p>} />
</Routes>
</div>
);
}
PostIndex > index.js
{
posts.map((post) => (
<NavLink
style={({ isActive }) => ({ color: isActive ? "red" : "black" })}
to={`/posts/${post.id}`}
state={{
post: posts.find((data) => data.id === post.id),
}}
>
<p>{post.title}</p>
</NavLink>
));
}
App.js
<Route path="albums" element={<Albums />}>
<Route index element={<AlbumList />} />
</Route>
Albums > index.js
function Albums() {
return (
<div>
<Outlet />
</div>
);
}
AlbumList > index.js
function AlbumList() {
const [albums, setAlbums] = useState();
const fetchData = async () => {
const { data } = await axios.get(
"https://jsonplaceholder.typicode.com/albums"
);
setAlbums(data);
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
{albums &&
albums.map((album) => (
// to={`${album.id}`}동일합니다.
<Link to={`/albums/${album.id}`}>
<p>{album.title}</p>
</Link>
))}
</div>
);
}