리액트 라우터 v6에는 더이상 switch가 존재하지 않습니다. 해당 기능을 Routes가 대신합니다.
기존의 v5에서는 Route 태그 내부에 컴포넌트를 넣는 것으로 Route 태그의 path 경로에서 컴포넌트가 렌더링 되었지만 이제는 element 속성에 컴포넌트를 넣어야 렌더링을 합니다. 또한 모든 Route는 Routes 안에서만 적용이 되므로, Route를 쓰기 위해서는 Routes 반드시 먼저 써줘야 합니다.
import { Routes, Route } from "react-router-dom";
const Welcome = () => {
return (
<section>
<h1>The Welcome Page</h1>
<Routes>
<Route path="/welcome/new-user" element={<p>Welcome, new user!</p>} />
</Routes>
</section>
);
};
export default Welcome;
v6 에서는 exact가 사라져도 해당 경로를 정확하게 읽을 수 있습니다.
import { Route, Routes } from "react-router-dom";
import Welcome from "./pages/Welcome";
import Products from "./pages/Products";
import ProductDetail from "./pages/ProductDetail";
import MainHeader from "./components/MainHeader";
function App() {
return (
<div>
<MainHeader />
<main>
<Routes>
<Route path="/welcome" element={<Welcome />} />
<Route path="/products" element={<Products />} exact />
<Route path="/products/:productId" element={<ProductDetail />} />
</Routes>
</main>
</div>
);
}
export default App;
만약 v5때 와 같이 시작하는 모든경로를 읽고자 한다면 추가로 /*을 붙여주면 됩니다. 단 위의 예시 코드에서는 그럼에도 불구하고 "/products/:productId" 까지 불러오지는 않습니다. v6에는 개선된 라우팅 알고리즘이 적용되어, 위의 route 3개 가운데 가장 적합한 path 경로를 알아서 설정하도록 개선되었기 때문에 해당 상황에서는 적용되지 않습니다.
v6에서는 Route의 순서가 중요하지 않게 되었습니다.
import { Route, Routes } from "react-router-dom";
import Welcome from "./pages/Welcome";
import Products from "./pages/Products";
import ProductDetail from "./pages/ProductDetail";
import MainHeader from "./components/MainHeader";
function App() {
return (
<div>
<MainHeader />
<main>
<Routes>
<Route path="/welcome" element={<Welcome />} />
<Route path="/products/*" element={<Products />} exact />
<Route path="/products/:productId" element={<ProductDetail />} />
</Routes>
</main>
</div>
);
}
export default App;
v6 에서는 NavLink에서 activeClassName이 사라졌습니다. 대신 어떠한 상황에서 특정 클래스를 추가하거나 style 속성을 지정하기를 원한다면, isActive라는 함수를 통해 설정하는 것이 가능합니다.
import { NavLink } from "react-router-dom";
import classes from "./MainHeader.module.css";
const MainHeader = () => {
return (
<header className={classes.header}>
<nav>
<ul>
<li>
<NavLink
className={(navData) => (navData.isActive ? classes.active : "")}
to="/welcome"
>
Welcome
</NavLink>
</li>
<li>
<NavLink
className={(navData) => (navData.isActive ? classes.active : "")}
to="/products"
>
Products
</NavLink>
</li>
</ul>
</nav>
</header>
);
};
export default MainHeader;
기존 Redirect의 기능은 Navigate로 바뀌게 되었습니다.
Navigate는 연속적으로 푸쉬 하거나 새롭게 대체하는 것이 가능합니다. 경로를 새롭게 대채 하는 경우에는 replace 속성을 추가하면 됩니다. replace 속성이 없다면 계속 기존 경로에서 이어 붙여 나가는 것이죠
<Routes>
<Route path="/"exact>
<Redirect to="/welcome">
</Route>
</Routes>
// =>
<Routes>
<Route path="/" element={<Navigate to="/welcome">}/>
</Routes>
v6의 중첩 라우터를 사용하기 위해서는 먼저, 최상위 경로가 모든 경로를 인지하도록 경로에 /* 설정을 해주어야 합니다.
<Route path="/welcome/*" element={<Welcome />} />
v6의 자식 라우터들은 모두 상위의 부모 라우터의 존속을 받게 됩니다. 예를 들어 최상위 부모 경로가 "/welcome/*" 이라면, 내부의 모든 중첩 경로들은 모두 기본적으로 /welcome 이라는 경로를 가지게 되는 것이죠. 따라서 v6의 중첩라우터는 이제 이런식으로 설정 되어야 합니다.
// 최상위 경로
<Route path="/welcome/*" element={<Welcome />} />
// 중첩 경로
const Welcome = () => {
return (
<section>
<h1>The Welcome Page</h1>
<Routes>
// 따라서 해당 경로는 /welcome/new-user 시 렌더링 된다.
<Route path="new-user" element={<p>Welcome, new user!</p>} />
</Routes>
</section>
);
};
export default Welcome;
해당 경로 설정은 Link에서도 그대로 적용됩니다.
import { Link, Routes, Route } from "react-router-dom";
const Welcome = () => {
return (
<section>
<h1>The Welcome Page</h1>
// /welcome/new-user
<Link to="new-user">new user</Link>
<Routes>
<Route path="new-user" element={<p>Welcome, new user!</p>} />
</Routes>
</section>
);
};
export default Welcome;
또한 중첩라우팅 시 이러한 접근 방식도 가능합니다.
// 상위 컴포넌트에서 중첩라우팅 설정하기
// 한 컴포넌트 안에서 모든 라우팅 설정이 가능해진다
function App() {
return (
<div>
<MainHeader />
<main>
<Routes>
<Route path="/" element={<Navigate to="/welcome" />} />
<Route path="/welcome/*" element={<Welcome />}>
<Route path="new-user" element={<p>Welcome, new user!</p>} />
</Route>
<Route path="/products" element={<Products />} />
<Route path="/products/:productId" element={<ProductDetail />} />
</Routes>
</main>
</div>
);
}
export default App;
단 이렇게 상위 컴포넌트에 중첩라우팅을 설정하는 경우 한 가지 고민사항이 생기게 됩니다. 컴포넌트의 어느부분에 중첩 컴포넌트를 렌더링해야 하는 부분입니다. 자기 중첩 컴포넌트를 렌더링 하고 싶은 곳이 맨위일 수도, 맨 아래일 수도, 버튼의 아래 일 수도 있으니까요. 따라서 이를 설정하는 방법이 필요합니다.
Outlet은 나타내고 싶은 컴포넌트 위치에 Outlet 컴포넌트를 만들어 놓으면, 해당 부분에 중첩 컴포넌트가 나타나게 됩니다.
import { Link, Outlet } from "react-router-dom";
const Welcome = () => {
return (
<section>
<h1>The Welcome Page</h1>
<Link to="new-user">new user</Link>
<Outlet />
</section>
);
};
export default Welcome;
기존의 v5에서는 브라우저의 history 객체에 접근하기 위해서는 useHistory() 를 사용하여, useHistory().replace() 혹은 useHistory().push() 등의 기능을 사용했습니다. v6에서는 useNavigate() 해당 기능을 대신합니다.
const navigate = useNavigate();
// replace()
navigate("/welcome", {replace: true});
// push()
navigate("/welcome");
// 이전 페이지로 이동 (뒤로가기)
navigate(-1);
// 앞으로 이동 (앞으로 가기)
navigate(1);
// 이전 페이지로 2번 이동 (뒤로가기)
navigate(-2);
v6 에서 잘못된 이동을 방지하는 기능인 Prompt 사라졌습니다. 따로 수정된 것도 아니라 실제 기능이 사라졌기 때문에, 이 기능을 수행하기를 원한다면 실제로 구현을 직접해야만 합니다.
사라지는데 명확한 이유는 없었고 그냥 우선순위에 밀려 뒤로 밀려났다고 합니다.
만약 잘못된 이동을 방지하는 Prompt가 매우 중점적인 기술이고, 이를 구현하는 것을 원하지 않는다면 v5를 그냥 쓰는 것도 대안이 될 수 있겠네요