在 2021 年 11 月初,React Router 正式釋出 v6 版本,身為 React 開發者已經按捺不住好奇心,想看看這個版本究竟增加了什麼功能?修改了什麼語法?因此就有了這篇文章的誕生。而在這篇文章中,我會介紹 v6 的一些新功能和 v5 有哪些地方不同。
v5:
<Switch>
<Route path="/about">
<About />
</Route>
<Route path="/topics">
<Topics />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
v6:
<Routes>
<Route path="/about" element={<About />} />
<Route path="/topics" element={<Topics />} />
<Route path="/" element={<Home />} />
</Routes>
在過去,需透過 exact 設定完全符合 url 時才會顯現指定的元件內容,現在 Router 會抓取最相近的 url 去呈現對應的元件。
<Routes>
<Route path="/about" element={<About />} />
<Route path="/topics" element={<Topics />} />
<Route path="/topics/:topic" element={<SpecTopics />} />
<Route path="/" element={<Home />} />
</Routes>
在 React Router v5 的官網有提到 activeClassName 被移除,v6 直接用 className 去判定即可。
<NavLink className={(navData) => navData.isActive ? "active" : "" } to="/about" />
之前的版本需要搭配 useRouteMatch 去組出巢狀路由,現在可以直接寫上想要的子路由上去。
v5:
export default function Topics() {
let match = useRouteMatch();
console.log(match);
return (
<div>
<ul>
<li>
<Link to={`${match.url}/components`}>Components</Link>
</li>
<li>
<Link to={`${match.url}/props-v-state`}>Props v. State</Link>
</li>
</ul>
<Switch>
<Route path={`${match.path}/:topicId`}>
<Topic />
</Route>
<Route path={match.path}>
<h3>Please select a topic.</h3>
</Route>
</Switch>
</div>
);
}
v6:
export default function Topics() {
return (
<div>
<h2>Topics</h2>
<ul>
<li>
<Link to="components">Components</Link>
</li>
<li>
<Link to="props-v-state">Props v. State</Link>
</li>
</ul>
<Routes>
<Route path=":topicId" element={<Topic />} />
<Route path="*" element={<h3>Please select a topic.</h3>} />
</Routes>
</div>
);
}
另外在 path 有沒有加上 '/' 結果都是一樣的:
// 路由為 `/hobby/favorite`
<Route
path="favorite"
element={<FavoriteHobbyListBody />}
/>
// 路由也為 `/hobby/favorite`
<Route
path="/favorite"
element={<FavoriteHobbyListBody />}
/>
在 Dashboard 元件內部,會根據路由 /messages
或 /tasks
在 <Outlet />
的地方呈現對應的元件 <DashboardMessages />
or <DashboardTasks>
。
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
{/* This element will render either <DashboardMessages> when the URL is
"/messages", <DashboardTasks> at "/tasks", or null if it is "/"
*/}
<Outlet />
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Dashboard />}>
<Route
path="messages"
element={<DashboardMessages />}
/>
<Route path="tasks" element={<DashboardTasks />} />
</Route>
</Routes>
);
}
useNavigate 的第一個參數可以是路由或是數字,代表前進或回去的頁數。
v5:
import { useHistory } from "react-router-dom";
const News = () => {
let history = useHistory();
return (
<>
<button onClick={()=> history.push("/home")}>Home</button>
</>
);
}
v6:
import { useNavigate } from "react-router-dom";
const News = () => {
let navigate = useNavigate();
return (
<>
<button onClick={()=> navigate('/home')}>Home</button>
</>
);
}
v5:
import { useHistory } from "react-router-dom";
function Exchanges() {
const { go, goBack, goForward } = useHistory();
return (
<>
<button onClick={() => go(-2)}>
2 steps back
</button>
<button onClick={goBack}>1 step back</button>
<button onClick={goForward}>1 step forward</button>
<button onClick={() => go(2)}>
2 steps forward
</button>
</>
);
}
v6:
import { useNavigate } from "react-router-dom";
function Exchanges() {
const navigate = useNavigate();
return (
<>
<button onClick={() => navigate(-2)}>
2 steps back
</button>
<button onClick={() => navigate(-1)}>1 step back</button>
<button onClick={() => navigate(1)}>
1 step forward
</button>
<button onClick={() => navigate(2)}>
2 steps forward
</button>
</>
);
}
import React from "react";
import { useRoutes } from "react-router-dom";
const App = () => {
let element = useRoutes([
{
path: "/",
element: <Dashboard />,
children: [
{
path: "messages",
element: <DashboardMessages />
},
{ path: "tasks", element: <DashboardTasks /> }
]
},
{ path: "team", element: <AboutPage /> }
]);
return element;
}
除了上述幾點之外,當然還有其他的變更,像是 React Router v6 整個 bundle size 縮小,更加輕量,不過以上就舉比較常用的幾點來說明,想了解更深入可以點擊我推薦的 youtube 影片或是到官網去閱讀文件囉!
最後,放上在鐵人賽 Day19 文章中介紹 React Router v5 的範例程式碼和修改後的 v6 版本程式碼範例提供給讀者做比較,不過範例中並沒有將文中的全部功能都使用上去,讀者可以自行練習看看哩!
v5 版本 codesandbox 範例
v6 版本 codesandbox 範例