從 React Router v5 過渡到 v6

前端晚間課 發表於 2022-01-14
React

React-routerreact js 中路由的標準庫。它允許 React 應用程式的使用者在應用程式的不同部分(元件)之間移動。

react-router 團隊 宣佈將 在 2021 年底釋出react-router 版本 6 (v6) 的穩定版本,但由於一些重大的 API 更改,從 react-router 版本 5 (v5) 切換到 v6 可能會很困難. 在本文中,我們將介紹 v6 中的新功能以及如何將現有的 React 專案從 v5 升級到 v6

要在我們的應用程式中升級 react-router 包的版本,我們導航到專案資料夾並執行

npm install [email protected][VERSION_NUMBER]

替換VERSION_NUMBER為我們要安裝的版本,或者如果我們想要最新版本,則替換為“ latest ”,如下所示:

npm install [email protected]

或者

npm install [email protected]

請注意,我們必須連線到網際網路才能完成安裝,否則安裝將失敗。另外,請確保專案中的 react 版本是 v16.8 或更高版本,因為 react-router v6 嚴重依賴於 react v16.8 最初支援的鉤子

Switch 被替換為 Routes

v5 時代的第一個被替代的是Switch元件。該Switch元件用於包裝我們的路由,它確保每次只載入一個匹配的路由。但這在 v6 中不再存在。我們使用Routes元件來替換Switch。請注意,我們仍然需要匯入BrowserRouter包裝我們的應用程式,就像在 v5 中所做的那樣。

在 v5 中,我們是這樣做:

import { BrowserRouter, Switch } from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <div className="App">
                <Switch>
                    {" "}
                    {/* 路由Route在此定義 */}
                </Switch>
            </div>
        </BrowserRouter>
    );
}
export default App

但在 v6 中,我們將這樣做

import { BrowserRouter, Routes } from "react-router-dom";

function App() {
    return (
        <BrowserRouter>
            <div className="App">
                <Routes>
                    {" "}
                    {/* Switch 會被改成 Routes */}
                    {/* 路由Route在此定義 */}
                </Routes>
            </div>
        </BrowserRouter>
    );
}

export default App

Route元件使用更新

儘管該Route元件在 v6 中仍然保留一個位置,但我們定義它的使用方式與我們在 v5 中的方式不同。我們將不再以 v5 中的任何方式放置我們想要渲染的元件,而是統一將其作為elementprop的值傳遞。

沒有exact配置

v5 中,不新增exact作為Route元件props的話,如果 URL 以 path 關鍵字開頭,則路徑將匹配,因為匹配過程是從上到下的順序。但在 v6 中,我們將不再需要該exact配置,因為路徑模式匹配演算法已更改,並且現在更加增強。

在 v5 中,我們這樣做了:

<Switch>
   {/* 三種Route元件使用定義 */}
   <Route path="/signup" component={Product} />
   {/* 或 */}
   {/* 這個方法允許我們將 props 傳遞給渲染的元件 */}
   <Route path="/games">
       <Product id={2} />
   </Route>
   {/* 或是通過render函式 */}
   <Route path="/games" render={(props) => <Product {...props} />} />
</Switch>

在 v6 中,

<Routes>
   {" "}
   <Route path="/games" element={<Product />} />
   {/* 帶有props的渲染元件 */}
   <Route path="/movies" element={<Product id={200} category="shirt" />} />
</Routes>

Links 和 NavLinks

LinkNavLink元件仍然可以執行在V6。Link元件使用與在 v5 的時候保持一樣,但使用NavLink元件時,刪除了activeClassNameactiveStyle prop。在 v5 中,activeClassNameprop 用於在連結啟用後自動將一些 CSS 類應用於連結,同時activeStyle允許我們在連結啟用時向連結新增內部樣式。

但是在 v6 中,我們現在可以使用一個函式來獲取有關連結活動狀態的資訊。該函式的引數是一個具有屬性的物件isActive。此屬性在連結處於活動狀態時為,在非活動時為isActive的值允許我們使用條件表示式來指示活動樣式或類名。

在 v5 中,我們這樣做了:

import {NavLink} from “react-router-dom”

{/* … */}
<NavLink
   to="/product"
   style={{ color: "#689" }}
   activeStyle={{ color: "#3072c9" }}
   className="nav_link"
   activeClassName="active"
>
   Products
</NavLink>

但在 v6 中,我們將這樣做:

<NavLink
   to="/product"
   style={({ isActive }) => ({ color: isActive ? "#3072c9" : "#689" })}
   className={({ isActive }) => `link${isActive ? " active" : ""}`}
>
   Product
</NavLink>

Navigate替代Redirect

在 v5 中,我們使用該Redirect元件將一個頁面帶到另一個頁面,但它不再從 v6 中的 react-router-dom 匯出。它已被Navigate元件替換。

在 v5 中,我們這樣做了:

<Route path="/faq">
   <Redirect to="/about" />
</Route>
<Route path="/about" component={About} />

但在 v6 中,我們將這樣做:

<Route path="/games" element={<Navigate to="/about" />} />;
<Route path="/games" element={<About />} />;

需要注意的是,如果我們只是按照Navigate上面程式碼片段中的方式新增元件,它只會將導航到該路徑的導航推送到導航堆疊中,但是如果我們打算用新頁面替換當前頁面,我們將 replace 屬性新增到Navigate元件中,如下所示:
<Route path="/games" element={<Navigate replace to="/about" />} />;

巢狀路由

顧名思義,巢狀路由是放置在另一個路由中的路由。它們用於在子元件中呈現更具體的資訊。在 v6 中,我們將巢狀路由放置為父路由的子路由。然後我們引入Outlet元件,它是從渲染元件中的 react-router-dom 匯出的,用於指定我們希望巢狀資訊顯示在哪裡。Outlet 元件不是必需的,但它使程式碼更清晰。
在 v5 中,我們這樣做了:

import { useRouteMatch } from "react-router-dom";
function App() {
   return (
       <BrowserRouter>
           <Switch>
               <Route exact path="/about" component={About} />
               <Route path="/product" component={Product} />
           </Switch>
       </BrowserRouter>
   );
}

function Product() {
   let match = useRouteMatch();
   return (
       <div>
           <Switch>
               {/* match.path 返回父路由中指定的路徑。在這種情況下,它是“/product" */}
               <Route path={`${match.path}`}>
                   <AllProducts />
               </Route>
               {/* 匹配 /product/:id */}
               <Route path={`${match.path}/:id`}>
                   <ProductDetail />
               </Route>
           </Switch>

       </div>
   );
}

在 v6 中,我們這樣做:

import { Outlet } from "react-router-dom";

function App() {
   return (
       <Routes>
           <Route path="/about" element={<About />} />
           <Route path="/product" element={<Product />}>
               {/* 這裡巢狀路由的路徑是相對於父路由的路徑的。 */}
               {/* 這裡變成 "/product/" */}
               <Route path="/" element={<AllProducts />} />
               {/* 這裡變成 "/product/:id" */}
               <Route path="/:id" element={<ProductDetail />} />

           </Route>
       </Routes>
   );
}

function Product() {
   return (
       <Container>
           <>
               <div>Product</div>
               {/* 父元件的其他內容 */}
           </>
           {/* 這是巢狀資訊開始的地方 */}
           <Outlet />
       </Container>
   );
}

程式化導航

當使用者因路徑上發生的事件(例如單擊按鈕、API 請求完成等)而被重定向時,就會發生程式化導航。在 v5 中,我們可以使用useHistory鉤子來執行以下操作:

import { useHistory } from "react-router-dom";

function Product() {
   const history = useHistory();

   const handleClick = () => {
       //這會將新路線推送到導航堆疊的頂部
       history.push("/new-route");

       //這會將當前路線替換為導航堆疊中的新路由
       history.replace("/new-route");
   };

   return (
       <div>
           <button>點選我重定向到新路由</button>
       </div>
   );
}

但是在 v6 中,useHistoryhook 被替換為useNavigatehook,並且我們以不同的方式使用它。

import { useNavigate } from "react-router-dom";

function Product() {
   const navigate = useNavigate();

   const handleClick = () => {
       //這會將新路線推送到導航堆疊的頂部
       navigate("/new-route");

       //這會將當前路線替換為導航堆疊中的新路由
       navigate("/new-route", { replace: true });
   };

   return (
       <div>
           <button>點選我重定向到新路由</button>
       </div>
   );
}

一件很酷的事情是我們可以在導航堆疊上任意前進和後退。通過使用正數作為上述引數navigate(),路由會向前移動該步數。負數向後做同樣的事情

// Goes forward
navigate(1)
// Goes forward twice
navigate(2)
// Goes backward
navigate(-1)
// Goes backward three times
navigate(-3)

刪除Prompt元件

Prompt如果有未儲存的更改,v5 中的元件可防止意外離開頁面。但是 react-router 團隊並沒有將它包含在 v6 中,也沒有替代方案。因此,如果你需要該功能,你可以手動實現它或返回到 v5。

除了不包括Prompt在當前版本(v6)中,useBlockerusePrompt都不起作用。react-router 團隊雖然在官方文件中表示,他們目前正在努力將其新增回 v6,但不是針對 6.x 的第一個穩定版本。

概括

讓我們強調一下我們所經歷的變化。

  • Switch 元件替換為 Routes 元件。
  • 如何放置 Route 的渲染元件的更改。
  • 路由中沒有exact。
  • activeClassName和activeStyle不存在於NavLink元件了.
  • 我們可以通過函式回撥訪問 NavLink 元件的 isActive 狀態。
  • Redirect元件已替換為Navigate元件。
  • 實現巢狀路由的一種更時尚的方式。
  • 沒有Prompt元件

總之,如果你還沒有準備好從 v5 或任何其他版本切換到 v6,你可以繼續使用它安裝以前的版本。

npm install [email protected][VERSION_NUMBER]

但是,你將錯過 v6 附帶的一些好東西,包括但不限於:

  • 增強的路徑模式匹配演算法。
  • 根據Bundlephobia,體積大小減少了 60%
  • 我相信我們能夠成功地切換到 react-router v6 並停止使用 Switch 元件(雙關語非常有意)😌。

    擁有更出色的編碼體驗🙌。