react-router-dom V6

Posted Monkey_Kcode

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了react-router-dom V6相关的知识,希望对你有一定的参考价值。

目录

1. 前言

2. 变更概览

将 Switch 升级为 Routes

路由匹配组件参数 由 component 改为 element

相对路径识别(子路由不需要补全父路由的path,react会自动补全)

用 useNavigate 替代  useHistory  

废弃 Redirect 标签,使用 Navigate 标签实现路由重定向

优化路由嵌套,添加 outlet 标签

使用 index 标识默认路由

添加 useResolvedPath  hooks 

添加 useSearchParams 读取和设置url参数

link 标签跳转的path 将支持 . 和 .. 这种语法(类比于 terminal 中的 cd .. 返回上级菜单 ) 

 path 通配符将只支持 * 和 :(以前的?等将不再支持) 

添加 useOutletContext 用于 路由之间共享状态


1. 前言

伴随React18的到来,React的路由也有 5.# 版本更新到了 V6版本,接下来让我们总结下 V6 版本的更新内容,对我们的使用有什么影响。

其实官网文档写的很清晰,我这里只是做个总结,大家也可直接移步官网: React Router | Overviewhttps://reactrouter.com/docs/en/v6/getting-started/overview

2. 变更概览

  • 将 Switch 升级为 Routes

  • 路由匹配组件参数 由 component 改为 element

// before V6 
<Switch>
    <Route path="/home" component=Home></Route>
</Switch>

// V6 
<Routes>
    // 注意,这里是 jsx 语法,需要配合标签, 传参也可以直接写为组件传参
    <Route path="/home" element=<Home animate=true />></Route>
</Routes>

  • 相对路径识别(子路由不需要补全父路由的path,react会自动补全)

<Routes>
   <Route path="user" element=<Invoices />>
       <Route path=":id" element=<Invoice /> />
       <Route path="me" element=<SentInvoices /> />
   </Route>
</Routes>

// path: /user
// path: /user/:id
// path: /user/me
  • 用 useNavigate 替代  useHistory  

// 函数组件使用编程式跳转

// V5
let history = useHistory();
history.push("/home");

// V6
let navigate = useNavigate();
navigate('/home')

// 如果需要类比 history.replace, 可以添加参数replace为true
navigate(to,  replace: true )

// 如果需要类比隐式传参,可以添加参数 state
navigate(to,  state )

// 同时 link 也添加了单独的参数 state
<Link to="/home" state=state />


// 如果需要类比 goBack,go等语法,也可直接在 navigate中 传层级参数
// 等价于 history.go(-1)
<button onClick=() => navigate(-2)>
    Go 2 pages back
</button>
<button onClick=() => navigate(-1)>Go back</button>
<button onClick=() => navigate(1)>
    Go forward
</button>
<button onClick=() => navigate(2)>
    Go 2 pages forward
</button>
  • 废弃 Redirect 标签,使用 Navigate 标签实现路由重定向

import  Navigate  from "react-router-dom";

function App() 
  return <Navigate to="/home" replace state=state />;


/*
    v5默认<Redirect />使用 replace 逻辑
    v6默认<Navigate />使用 push 逻辑 ,可以通过参数设置为 replace
*/
  • 优化路由嵌套,添加 outlet 标签

import 
    Routes,
    Route,
    Link,
    Outlet,
    BrowserRouter
   from "react-router-dom";
  
  function Layout() 
    return (
      <div>
        <h1>Welcome to the V6!</h1>
        <nav>
          <Link to="product">产品页</Link>
          <br/>
          <Link to="detail">详情页</Link>
        </nav>
        <div className="content">
  
          /* 子路由将会显示在这里,用outlet占位 */
          <Outlet />
  
        </div>
      </div>
    );
  
  
  function Product() 
    return <h1>产品页</h1>;
  
  
  function Detail() 
    return <h1>详情页</h1>;
  

  function App() 
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element=<Layout />>
                    <Route path="product" element=<Product /> />
                    <Route path="detail" element=<Detail /> />
                </Route>
            </Routes>
        </BrowserRouter>
    );
  
  
  export default App
  • 使用 index 标识默认路由

<Routes>
   <Route path="/" element=<Layout />>
       <Route index element=<Activity /> />
       <Route path="invoices" element=<Invoices /> />
       <Route path="activity" element=<Activity /> />
   </Route>
</Routes>
  • 添加 useResolvedPath  hooks 

在 V5版本的文档中,只有四个比较重要的hooks,分别是 useHistory, useLocation, useParams, useRouteMatch,V5文档:

React Router: Declarative Routing for React.jshttps://v5.reactrouter.com/web/api/Hooks/useparams而 V6 版本又添加了一些hooks,我们简单列举几个可能会用到的,完整版移步官网:

https://reactrouter.com/docs/en/v6/api#resolvepathhttps://reactrouter.com/docs/en/v6/api#resolvepath

由于V6的相对路径识别特性,有时我们需要获取完整的url路径,可以使用 useRelovedPath

useRelovedPath 必须接收一个参数,可以为空字符串

  <Route path="/" element=<Layout />>
       <Route path="product" element=<Product /> />
       <Route path="detail" element=<Detail /> />
  </Route>

  function Product() 
    const path = useResolvedPath('id');
    console.log(path);  // output:  pathname: '/product/id' 
    return <h1>产品页</h1>;
  
  
  function Detail() 
    const path = useResolvedPath('');
    console.log(path);   // output:  pathname: '/detail' 
    return <h1>详情页</h1>;
  
  • 添加 useSearchParams 读取和设置url参数

useSerachParams 可以读取和修改当前位置url的查询参数(?id=123), 具体使用方式类比于 useState,但用法略有不同。

获取某个searchParams: searchParams.get(key)

设置某个searchParams: setSearchParams(key:value)

import 
    Routes,
    Route,
    Link,
    Outlet,
    BrowserRouter,
    useResolvedPath,
    useSearchParams
   from "react-router-dom";
  
  function Layout() 
    return (
      <div>
        <h1>Welcome to the V6!</h1>
        <nav>
          <Link to="product">产品页</Link>
          <Link to="detail?id=123">详情页</Link>
        </nav>
        <div className="content">
  
          <Outlet />
  
        </div>
      </div>
    );
  
  
  function Product() 
    const path = useResolvedPath('id');
    console.log(path);
    return <h1>产品页</h1>;
  
  
  function Detail() 
    const [searchParams,setSearchParams] = useSearchParams()

    const handleSubmit = ()=>
        // 输入键值对,设置对应的 search 参数
        setSearchParams(id:456)
    

    // 通过 get 方法获取key对应的value
    console.log(searchParams.get('id'));

    return (
        <h1>
            详情页 : searchParams.get('id') 
            <br/>
            <button onClick=()=>handleSubmit()>update searchParams</button>
        </h1>
    );
  

  function App() 
    return (
        <BrowserRouter>
            <Routes>
                <Route path="/" element=<Layout />>
                    <Route path="product" element=<Product /> />
                    <Route path="detail" element=<Detail /> />
                </Route>
            </Routes>
        </BrowserRouter>
    );
  
  
  export default App
  • link 标签跳转的path 将支持 . 和 .. 这种语法(类比于 terminal 中的 cd .. 返回上级菜单 ) 

// 这里直接拿了官网的示例

function App() 
  return (
   <BrowserRouter>
     <Routes>
       <Route path="users" element=<Users />>
         <Route path=":id" element=<UserProfile /> />
       </Route>
     </Routes>
   <BrowserRouter>

  );


function Users() 
  return (
    <div>
      <h2>
        /* This links to /users - the current route */
        <Link to=".">Users</Link>
      </h2>

      <ul>
        users.map((user) => (
          <li>
            /* This links to /users/:id - the child route */
            <Link to=user.id>user.name</Link>
          </li>
        ))
      </ul>
    </div>
  );


function UserProfile() 
  return (
    <div>
      <h2>
        /* This links to /users - the parent route */
        <Link to="..">All Users</Link>
      </h2>

      <h2>
        /* This links to /users/:id - the current route */
        <Link to=".">User Profile</Link>
      </h2>

      <h2>
        /* This links to /users/mj - a "sibling" route */
        <Link to="../mj">MJ</Link>
      </h2>
    </div>
  );
  •  path 通配符将只支持 * 和 :(以前的?等将不再支持) 

// 这里直接拿了官网的例子,让我们看下 * 的作用(子孙路由)
import 
  BrowserRouter,
  Routes,
  Route,
  Link,
 from "react-router-dom";

function App() 
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element=<Home /> />
        <Route path="users/*" element=<Users /> />
      </Routes>
    </BrowserRouter>
  );


function Users() 
  return (
    <div>
      <nav>
        // path: user/me
        <Link to="me">My Profile</Link>
      </nav>

      <Routes>
        // path:  user/:id
        <Route path=":id" element=<UserProfile /> />

        // path:  user/me
        <Route path="me" element=<OwnUserProfile /> />
      </Routes>
    </div>
  );
  • 添加 useOutletContext 用于 路由之间共享状态

我们可以用 useOutletContext 在子路由与父路由之间共享一些值

function Parent() 
  const [count, setCount] = React.useState(0);
  return <Outlet context=[count, setCount] />;



import  useOutletContext  from "react-router-dom";

function Child() 
  const [count, setCount] = useOutletContext();
  const increment = () => setCount((c) => c + 1);
  return <button onClick=increment>count</button>;

react-router-dom v^4路由配置

首先安装路由

  npm install --save react-router-dom

新建一个router.js文件

技术分享图片

然后我们的router.js代码如下↓

 1 import React from ‘react‘
 2 //这里的HashRouter是一个的路由的模式,它分为两种BrowserRouter以及HashRouter两种模式  使用的方法只是在导入时有区别,后面的代码完全一致即可
 3 import {HashRouter as Router, Route, Switch} from ‘react-router-dom‘  
 4 import AppComponent from ‘../pages/App‘
 5 import NewsComponent from ‘../pages/news‘
 6 import DetailComponent from ‘../pages/details‘
 7 import {AuthRoute} from ‘../assets/common/function‘
 8 
 9 export default class RouteComponent extends React.Component {
10     render() {
11         return (
12             <div>
13                 <React.Fragment>//用于清除多出的div  不太明白他的作用的可以在浏览器F12查看一下即可
14                     <Router>
15                         <React.Fragment>
16                             <Switch>//只找到第一个被匹配的路由
17                                 <Route path=‘/‘ exact component={AppComponent}></Route>//exact 表示完全匹配
18                                 <Route path=‘/news‘ exact component={NewsComponent}></Route>
19                                 <AuthRoute path=‘/detail‘ component={DetailComponent}></AuthRoute>//AuthRoute是使用的会员登录在以后的文章中会在详细介绍,这里改成Route即可
20 </Switch> 21 </React.Fragment> 22 </Router> 23 </React.Fragment> 24 </div> 25 ); 26 } 27 }

然后我们去index.js文件下改变一下显示的组件

1 import RouteComponent from ‘./router/router‘;
2 ReactDOM.render(<Index />, document.getElementById(‘root‘));
3 registerServiceWorker();

现在我们的基本路由就以及配置完成了,当然我们不能去URL地址栏中修改地址,

来说一下路由的跳转,目前我了解的有三种方式进行跳转

1、push方法

2、replace方法

3、Link方法

下面来详细的介绍一下他们的用法

一、push

1 <button type="button" onClick={this.pushPage.bind(this,‘/news‘)}>push</button>
2 pushPage(path){
3   this.props.history.push(path) 
4 }

二、replace

1 <button type="button" onClick={this.replactPage.bind(this,‘/news‘)}>replact</button>
2   replactPage(path){
3     this.props.history.replact(path) 
4  }

三、Link

这个方法先需要我们在引入一下

1 import {Link} from “react-router-dom”
2 <Link to=‘/news‘>Link</Link>

现在我们的一级路由就差不多配置完成了,接下来那我们扩展一下二级路由的使用以及带参路由

根据我个人的理解其实二级路由就是在你的一个路由页面在重复的写一遍router.js里面的内容

让我们看一下代码

 1 render() {
 2         return (
 3             <div className={‘App‘}>
 4                 <div className={‘nav‘}>
 5                     <dl onClick={this.goPage.bind(this,‘/home‘)}>
 6                         <dt><i className={‘iconfont icon-home‘}></i></dt>
 7                         <dd>首页</dd>
 8                     </dl>
 9                     <dl onClick={this.goPage.bind(this,‘/fenlei‘)}>
10                         <dt><i className={‘iconfont icon-fenlei‘}></i></dt>
11                         <dd>分类</dd>
12                     </dl>
13                 </div>
14                 <Switch>
15                     <Route path={‘/home‘} component={Home}></Route>
16                     <Route path={‘/fenlei‘} component={Fenlei}></Route>
17                 </Switch>
18             </div>
19         );
20     }
1 goPage(path){
2         this.props.history.replace(path)
3     }

注:如果我们的路由是在另一个组件内引入进来的,我们需要如下操作

1 import { withRouter } from ‘react-router‘;
2 export default withRouter(App);

这个样我们的二级路由也可以算是实现了

带参路由

<button onClick={this.goPage.bind(this,‘/news?cid=你需要传递的参数&name=...‘)}>带参路由</button>

跳转方式和上面说的是一样的

我们传递了参数在另一个页面的就需要接受它的参数

这里我新建了一个页面来定义了一个正则

function localParam (search, hash) {
    search = search || window.location.search;
    hash = hash || window.location.hash;
    var fn = function(str, reg) {
        if (str) {
            var data = {};
            str.replace(reg, function($0, $1, $2, $3) {
                data[$1] = $3;
            });
            return data;
        }
    }
    return {
        search : fn(search, new RegExp("([^?=&]+)(=([^&]*))?", "g")) || {},
        hash : fn(hash, new RegExp("([^#=&]+)(=([^&]*))?", "g")) || {}
    };
}

export {
    localParam
}

在接受值的页面引入

import {localParam} from "../assets/js/function"

这里我们使用componentWillReactiveProps生命周期接受

componentWillReceiveProps (nextprops){
        var get = localParam(nextprops.location.search).search
        var cid = get.cid
        console.log(cid)
}        

技术分享图片

我们可以看见控制台以及可以打印出来参数

好的,现在我们的一个完整路由已经差不多完成了

有什么不足或者不对的地方欢迎大家指出



以上是关于react-router-dom V6的主要内容,如果未能解决你的问题,请参考以下文章

react-router-dom v5和react-router-dom v6区别

反应:'重定向'不是从'react-router-dom'导出的

尝试导入错误:“Switch”未从“react-router-dom”导出

React-router-dom (v6) 和 Framer Motion (v4)

React使用react-router-dom配置路由

“react-router-dom”中的 useLocation 返回意外对象