我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

react路由的useMatch()钩子,是用来重新获取隐藏在location.pathname中的参数的。而React路由的useMatches()钩子呢?是用来干什么的呢?这就是本文考虑的主要问题。表面上来看,match,一个是单数,另外一个是复数es。请在本文中寻找更多的区别可能性。

苏南大叔:React路由,如何理解useMatches()钩子?匹配结果数组组成 - 路由匹配结果
React路由,如何理解useMatches()钩子?匹配结果数组组成(图2-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0create-react-app@5.0.1react-router-dom@6.27.0react@18.3.1useMatches()的结果,主要反馈了使用了几条路由。

useMatches()

useMatches()官方文档地址:

主要功能就是返回当前location匹配的路由情况。它并没有需要传递的参数,执行返回个数组,显示一个和路由定义比较相似的结果。

简单例子

这个例子基于create-react-appcra模版修改,执行命令:

create-react-app test
cd test
npm i react-router-dom --save
npm i @babel/plugin-proposal-private-property-in-object --save-dev
npm i nodemon --save-dev
npm i esno --save-dev

这里使用一个最简单的例子,参考修改如下:
src/routes.js

import React from "react";
import Post from "./Post";
import Detail from "./Detail";
const routers = [
  {
    path: "/detail/:id",
    element: <Detail />,
    author: "苏南大叔"
  },
  {
    path: "/post/",
    element: <Post />,
    children: [
      {
        path: ":id",
        element: <Detail />,
      },
    ],
  },
];
export default routers;

src/App.js【重点】:

import routes from "./routes";
import { RouterProvider, createBrowserRouter } from "react-router-dom";
const router = createBrowserRouter(routes);
function App() {
  return <RouterProvider router={router}></RouterProvider>;
}
export default App;

src/Post.js

import { useMatches, Outlet } from "react-router-dom";
export default () => {
  const matches = useMatches();
  console.log(matches);
  return (
    <div>
      Post组件
      <Outlet />
    </div>
  );
};

src/Detail.js:

import { useMatches } from "react-router-dom";
export default () => {
  const matches = useMatches();
  console.log(matches);
  return <div>Detail组件</div>;
}; 

为了对比说明问题,这里的Post.jsDetail.js代码几乎一样,区别仅仅在于Post.js支持<Outlet/>(方便把<Detail/>嵌套进去,使得相关代码可以“同时”执行在父子组件里面)。

苏南大叔:React路由,如何理解useMatches()钩子?匹配结果数组组成 - 核心代码
React路由,如何理解useMatches()钩子?匹配结果数组组成(图2-2)

关键代码

useMatches()关键代码,如下:

import { useMatches } from "react-router-dom";
export default () => {
  const matches = useMatches();
  //...
}; 

正常使用的话,还需要必须保证这个组件在createXxxRouter()函数返回的路由里面。

唯一路由写法

其实就是有关上面App.js这个重点文件的写法。react路由大体上有两种写法,这个useMatches()罕见的仅仅支持其中一种方式。与此同时,useMatch()是支持两种写法的。

useMatches()只支持createXxxRouter创建的data router,不支持<Routes><Route/></Routes>创建的路由。和<Routes>搭配使用的时候,会出现下面的错误提示:

ERROR
useMatches must be used within a data router.  See https://reactrouter.com/routers/picking-a-router.
    at invariant (http://localhost:3000/static/js/bundle.js:903:11)
    at useDataRouterState (http://localhost:3000/static/js/bundle.js:39719:87)
    at useMatches (http://localhost:3000/static/js/bundle.js:39773:7)
    at Post (http://localhost:3000/static/js/bundle.js:137:79)
    at renderWithHooks (http://localhost:3000/static/js/bundle.js:24375:22)
    at mountIndeterminateComponent (http://localhost:3000/static/js/bundle.js:28346:17)
    at beginWork (http://localhost:3000/static/js/bundle.js:29649:20)
    at HTMLUnknownElement.callCallback (http://localhost:3000/static/js/bundle.js:14631:18)
    at Object.invokeGuardedCallbackDev (http://localhost:3000/static/js/bundle.js:14675:20)
    at invokeGuardedCallback (http://localhost:3000/static/js/bundle.js:14732:35)

也就是说,必须使用“路由数据配置”+createXxxRouter()这样的组合。参考文章:

路由数组中的自定义值

实际的路由信息是:

{
  path: "/detail/:id",
  element: <Detail />,
  author: "苏南大叔"
},

写在routes信息数组里面的自定义值author,是拿不到的。匹配到的useMatches()结果(复数数组)里面,得到的组成对象元素是:

{
  data: undefined
  handle: undefined
  id: "1"
  params: {id: '123'}
  pathname: "/post"
}

对比一下useMatch()的匹配结果(单数)组成:

{
  params: {page3: 'post', id3: '123', title3: 'demo'}
  pathname: "/post/123/demo"
  pathnameBase: "/post/123/demo"
  pattern: {caseSensitive:false,end:true,path:"/:page3/:id3/:title3"}
}
useMatch(path)useMatches()
支持两种路由写法仅仅支持Provider写法
paramsparams
pathnamepathname
pathnameBase×
pattern×
×id
×handle
×data

这里的handledata是有特殊作用的,后续文章待议。

可见,useMatches()的匹配结果,虽然来源自routes.js里面的路由定义。但是,两者并不完全相同。其中相同的地方是:paramspathnameuseMatch()是主观输入为匹配对象的单数输出。useMatches()是以客观事实为基础的复数输出。

父子组件得到相同结果

在父组件里面执行和在子组件里面执行,得到的useMatches()结果都是一致的。在执行/post/123的时候,父组件是<Post/>,子组件是<Detail/>。两者的useMatches()结果完全一致。均为:

[
    {
        "id": "1",
        "pathname": "/post",
        "params": {
            "id": "123"
        }
    },
    {
        "id": "1-0",
        "pathname": "/post/123",
        "params": {
            "id": "123"
        }
    }
]

使用useLocation()进行查找的时候,得到的结果也是一致的。这反馈出,useMatches()返回的结果,并非是对应的路由定义,而是一个整体不变的路由匹配结果。

对比总结

  • useParams()是用来和路由定义配套获取location里面的参数的。
  • useMatch()是用来重新匹配location.pathname,重新定义参数的。
  • useMatches()是用来获得当前的location.pathname,都命中了那几条路由结果的。

题外话

复数变单数

useMatches()的结果是个复数,那么,最最最贴合当前location的结果是哪一条呢?可以使用下面的写法获得:

import { useLocation } from "react-router-dom";
const location = useLocation();
const match = matches.find((m) => m.pathname == location.pathname);
console.log(match);

路由path

路由嵌套,需要的是outlet
路由分离式嵌套,需要的也是outlet,并且需要path*

参考文章:

结语

从上述内容中可以看到:useMatches()useMatch()这两个react router的钩子,虽然拼写很相似。但是,功用上是有很大不同的,返回值的差异也很巨大。

更多苏南大叔的react经验文章,请参考:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   react