iT邦幫忙

2021 iThome 鐵人賽

2
Modern Web

用30天更加認識 React.js 這個好朋友系列 第 31

[Bonus 系列] - 來看看 React Router v6 有什麼新功能?和 v5 有哪些地方不同?

在 2021 年 11 月初,React Router 正式釋出 v6 版本,身為 React 開發者已經按捺不住好奇心,想看看這個版本究竟增加了什麼功能?修改了什麼語法?因此就有了這篇文章的誕生。而在這篇文章中,我會介紹 v6 的一些新功能和 v5 有哪些地方不同。

1. Switch 元件被 Routes 取代,傳遞 props 元件的方式也做了調整,出現了新的 props,element

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>

2. Router 變聰明了,不用再加上 exact

在過去,需透過 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>

3. NavLink 的 activeClassName prop 被移除

React Router v5 的官網有提到 activeClassName 被移除,v6 直接用 className 去判定即可。

<NavLink className={(navData) => navData.isActive ? "active" : "" } to="/about" />

4. 簡化巢狀路由、推出 Outlet API

之前的版本需要搭配 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 />}
/>

Outlet api 也是建立巢狀路由的一大利器,以下為官方提供的範例。

在 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>
  );
}

5. useNavigate 代替了 useHistory

useNavigate 的第一個參數可以是路由或是數字,代表前進或回去的頁數。

範例1:

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>
    </>
  );
}

範例2:

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>
    </>
  );
}

除了 useNavigate hook 外,v6 也提供了 元件。

6. useRoutes 代替 react-router-config

useRoutes 官網說明

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;
}

7. 其他功能

除了上述幾點之外,當然還有其他的變更,像是 React Router v6 整個 bundle size 縮小,更加輕量,不過以上就舉比較常用的幾點來說明,想了解更深入可以點擊我推薦的 youtube 影片或是到官網去閱讀文件囉!


最後,放上在鐵人賽 Day19 文章中介紹 React Router v5 的範例程式碼和修改後的 v6 版本程式碼範例提供給讀者做比較,不過範例中並沒有將文中的全部功能都使用上去,讀者可以自行練習看看哩!

v5 版本 codesandbox 範例
v6 版本 codesandbox 範例


上一篇
Day30-還想學更多嗎?推薦 Youtube 上面免費的 React 學習資源
系列文
用30天更加認識 React.js 這個好朋友31

尚未有邦友留言

立即登入留言