react-router@6 版本初体验

2022年5月11日11:45:57

最近使用了一下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)

  • 作者:阿政想暴富
  • 原文链接:https://www.cnblogs.com/kongyijilafumi/p/16203907.html
    更新时间:2022年5月11日11:45:57 ,共 7038 字。