react-router@6 版本初體驗

阿政想暴富發表於2022-04-28

最近使用了一下react-router@6 版本感覺有很大的改動,記錄一下。

React Router v6 makes heavy use of React hooks, so you'll need to be on React 16.8 or greater before attempting the upgrade to React Router v6. The good news is that React Router v5 is compatible with React >= 15, so if you're on v5 (or v4) you should be able to upgrade React without touching any of your router code.

官網給出的原文上面說的很清楚了,在 6 版本上使用了React Hooks寫法,說明不支援class類來寫。並且要求react版本在16.8以上。如果想相容class元件 以及 react@15使用只能用react-router@5版本

安裝

D:\your_project> npm i react-router-dom@6
# or
D:\your_project> yarn add react-router-dom@6
# or
D:\your_project> cnpm i react-router-dom@6

變化

SwitchRoutes

在 v5 版本中必須明確的說明巢狀的的路由和連結,那麼就要獲取父路由的屬性等等。在 v6 版本則不需要,因為路徑是相對的。案例如下:

// react-router-dom v5.1
import {
  BrowserRouter,
  Switch,
  Route,
  Link,
  useRouteMatch,
} from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Switch>
        <Route exact path="/" children={<Home />} />
        <Route path="/users" children={<Users />} />
      </Switch>
    </BrowserRouter>
  );
}

function Users() {
  // 獲取當前 router 匹配資訊  { path:string , url: string , }
  const match = useRouteMatch();
  return (
    <div>
      <div className="link">
        <Link to={`${match.url}/me`}>My Profile</Link>
      </div>
      <Switch>
        {/*  path =  /users/me   */}
        <Route path={`${match.path}/me`}>
          <OwnUserProfile />
        </Route>
        {/*  path =  /users/:id   */}
        <Route path={`${match.path}/:id`}>
          <UserProfile />
        </Route>
      </Switch>
    </div>
  );
}

在 v6 版本後 再父元件裡寫的路徑若無 /開頭 都是相對父元件。案例如下:

// react-router-dom v6+
import {
  BrowserRouter,
  Routes,
  Route,
  Link,
} from "react-router-dom";

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<Home />} />
        {/* /users 下的所有路徑 渲染 User元件 /user  /user/*  */}
        <Route path="/users/*" element={<Users />} />
      </Routes>
    </BrowserRouter>
  );
}

function Users() {
  return (
    <div>
      <nav>
        { /* to = /users/me  */ }
        <Link to="me">My Profile</Link>
      </nav>
      {/*  子元件 path 直接相對父元件 */}
      <Routes>
        {/*  /user/me  */}
        <Route path="me" element={<OwnUserProfile />} />
        {/*  /user/:id  */}
        <Route path=":id" element={<UserProfile />} />
      </Routes>
    </div>
  );

Route

  • Route Props.path

是相對的,它們會自動在父路由的路徑和 URL 上構建。

<BrowserRouter>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="users" element={<Users />}>
      {/* /user/me */}
      <Route path="me" element={<OwnUserProfile />} />
      {/* /user/:id */}
      <Route path=":id" element={<UserProfile />} />
    </Route>
  </Routes>
</BrowserRouter>
  • Route (Props.component | Props.render | Props.children) 廢棄 -> 使用 Props.element

在 v5 版本 路由可以使用 三種屬性來掛載元件,但是在 v6 版本 只有一種 就是 element屬性。

// react-router-dom v5.x
function Page() {
  return <div> page 元件 </div>;
}

// 使用  component 掛載元件
<Route path="/page" component={Page} />;

// 使用  render 掛載元件
<Route
  path="/page"
  render={(routerProps) => <Page routerProps={routerProps} />}
/>;

// 使用 children 掛載元件
<Route path="/page" children={<Page />} />;

<Route path="/page">
  <Page />
</Route>;

Redirect (移除)

在 v5 版本中 <Switch> 裡 使用 <Redirect> 元件進行路由重定向。

// v5.x

// any code ...
<Switch>
  <Route path="/user" children={<User />} />
</Switch>;

function User({ userInfo }) {
  if (!userInfo) {
    // 重定向 到 登入頁
    return <Redirect to="/login" />;
  }
  return <div>User Page</div>;
}

在 v6 版本中 將使用 Navigation

// v6.x

// any code ...
<Switch>
  <Route path="/user" children={<User />} />
</Switch>;

function User({ userInfo }) {
  if (!userInfo) {
    // 重定向 到 登入頁
    return <Navigation to="/login" />;
  }
  return <div>User Page</div>;
}

Switch (移除)

在 v5 版本中 用 <Switch>元件包裹 <Route> 路由元件, 現在改用 <Routes>

// v5.x
<Switch>
  <Route path="/" children={<Home />} />
  <Route path="/user" children={<User />} />
</Switch>

// v6.x
<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/user" element={<User />} />
</Routes>

Link 元件主要用於路由跳轉,在 v5 版本 Linkto 屬性是根據當前 url。舉例:

// 當前 路由路徑 /user
<Link to="me" /> // render <a href="/me" >...</a>

// 當前 路由路徑 /user/
<Link to="me" /> // render <a href="/user/me" >...</a>

而在 v6 版本中 如果LinkRoute裡渲染 to 屬性是根據當前<Route>路由的匹配的 url;如果不在Route元件裡掛載的,則是根據 BrowserRouter.basename 渲染 跳轉路徑,預設/

// v6.x
import { BrowserRouter, Link, Outlet, Route, Routes } from "react-router-dom";
<BrowserRouter>
  <Link to=""> go Home</Link> | <Link to="user"> go user</Link>
  <Routes>
    <Route path="/" element={<Home />} />
    <Route path="/user" element={<User />}>
      <Route path=":id" element={<Person />} />
    </Route>
  </Routes>
</BrowserRouter>;

function Home() {
  return (
    <div>
      <h1>Home page</h1>
    </div>
  );
}
function User() {
  return (
    <div>
      <h1>User page</h1>
      <Link to="..">go Home</Link>
      <Link to="abc">go User abc</Link>
      <hr />
      <Outlet />
    </div>
  );
}

function Person() {
  return (
    <div>
      <h1>User abc page</h1>
      <Link to="..">go user</Link>
      <Link to="../..">go Home</Link>
    </div>
  );
}

如果你把 url 看作檔案路徑,你發現 Linkto 相當於 cd 跳轉。

<Route path="app">
  <Route path="dashboard">
    <Route path="stats" />
  </Route>
</Route>
<Link to="stats"/>             //  => <a href="/app/dashboard/stats">
<Link to="../stats"/>          //  => <a href="/app/stats">
<Link to="../../stats"/>       //  => <a href="/stats">
<Link to="../../../stats"/>    //  => <a href="/stats">

假設當前的檔案路徑為 /app/dashboard

cd stats                        # pwd is /app/dashboard/stats
cd ../stats                     # pwd is /app/stats
cd ../../stats                  # pwd is /stats
cd ../../../stats               # pwd is /stats

Props.exact 命名為 Props.end,刪除Props.activeClassName,Props.activeStyle 使用函式返回style,className

// v5.x
const style = { color : "red" }
<NavLink to="/user" exact />
<NavLink to="/user" activeClassName="active-link" />
<NavLink to="/user" activeStyle={style} />

// v6.x
const getCls = ({ isActive  }) => isActive ? "link" : "ative link";
const getStyle = ({ isActive  }) => ({ color: isActive ?'red': 'blue' })
<NavLink to="/user" end /> // 精確匹配
<NavLink to="/user" className={getCls} />
<NavLink to="/user" activeStyle={getStyle} />

useRouteMatch (替換) useMatch

useMatch api

新增

<Navigate> 元件和 useNavigate hooks api 擁有一樣的用法。在函式元件使用 useNavigate api,而在 class 元件,可以使用 Navigate 元件進行掛載跳轉路由。

interface NavigateProps {
  to: To;
  replace?: boolean;
  state?: any;
}
class User extends React.Component {
  render() {
    const { userInfo } = this.props;
    if (!userInfo) {
      return <Navigate to="/login" replace />;
    }
    return <div>User</div>;
  }
}

Outlet 路由檢視元件

Route 下有子 Route 的時候 需要使用 Outlet 顯示子路由內容。

案例

useLocation hooks

獲取當前路由位置物件。如果您想在當前位置更改時執行一些副作用,這可能很有用。

import { useEffect } from "react"
import { useLocation } from 'react-router-dom';

function App() {
  let location = useLocation();

  useEffect(() => {
   // do  something
  }, [location]);

  return (
    // ...
  );
}

useNavigate hooks

呼叫返回一個函式,該函式呼叫第一個引數為string型別 與 <Link> to 屬性 效果一樣,為number型別,在歷史記錄堆疊中傳遞要訪問的增量。第二個引數 與 <Link>state,replace型別相同。 替代了 v5 版本中的 history 物件

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

function App(){
  const navigate = useNavigate();
  const submit = (data)=>{
    api(data).then(res=>{
      navigate("/user")
    })
  }
  const back = ()=>{
    navigate(-1) // 後退一頁
  }
  return (
    // ...
  )
}

useParams hooks

獲取路由:匹配的資訊,返回當前 URL 中與 匹配的動態引數的鍵/值對的物件。

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

function Person(){
  const { id } = useParams() //  path =  /user/:id  =>  current url = /user/1

  console.log(id) // 1
  return (
    // ....
  )
}

useRoutes hooks

使用陣列來定義路由。返回路由元件。

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

const routes = [
  {
    path: "/",
    element: <Home />,
    children: [
      {
        path: "/test",
        element: <Test />,
      },
    ],
  },
  {
    path: "*",
    element: <NotFound />,
  },
];

function App() {
  const router = useRoutes(routes);
  return router;
}

useSearchParams hooks

用於讀取和設定 url 的query部分,返回和 react useState hooks 一樣。每次searchParams修改相當於路由 push 一次。

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

// current url => /
function App() {
  const [searchParams, setSearchParams] = useSearchParams();
  const setQueryId = () => {
    // url change => /?id=2&name=test
    setSearchParams({
      id: 2,
      name: "test",
    });
  };
  return (
    <div>
      <h1> current query id :{searchParams.get("id")}</h1>
      <button onClick={setQueryId}>set id</button>
    </div>
  );
}
  • searchParams

    • searchParams.get(str) 返回 string | null

      獲取 url 的 query 第一個匹配上的的str值.例如/user?id=1=> searchParams.getAll('id') 返回值為為 '1'

    • searchParams.getAll(str) 返回 string[]

      獲取 url 的 query 所有的str值.例如/user?id=1&id=2=> searchParams.getAll('id') 返回值為 ['1','2']

  • setSearchParams

    修改當前 url query 部分。相當於路由跳轉了。你可以使用第二個引數,進行是否推入history棧。例如:setSearchParams({ id : 2 },{ replace : true })相當於history.replace({currentUrl}/?id=2)

相關文章