React路由,如何理解useMatches()钩子?匹配结果数组组成
发布于 作者:苏南大叔 来源:程序如此灵动~
react路由的useMatch()钩子,是用来重新获取隐藏在location.pathname中的参数的。而React路由的useMatches()钩子呢?是用来干什么的呢?这就是本文考虑的主要问题。表面上来看,两个钩子名称里面的单词拼写match,一个是单数,另外一个是复数es。请在本文中寻找更多的区别可能性。

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0,create-react-app@5.0.1,react-router-dom@6.27.0,react@18.3.1。useMatches()的结果,主要反馈了使用了几条路由。
useMatches()
useMatches()官方文档地址:
主要功能就是返回当前location匹配的路由情况。
- 它并没有需要传递的参数。
- 执行后会返回个数组,显示一个和路由定义比较相似的结果。
简单例子
这个例子基于create-react-app的cra模版修改,执行命令:
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 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.js和Detail.js代码几乎一样,区别仅仅在于Post.js支持<Outlet/>(方便把<Detail/>嵌套进去,使得相关代码可以“同时”执行在父子组件里面)。

关键代码
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写法 |
| params | params |
| pathname | pathname |
| pathnameBase | × |
| pattern | × |
| × | id |
| × | handle |
| × | data |
这里的handle和data是有特殊作用的,后续文章待议。
可见,useMatches()的匹配结果,虽然来源自routes.js里面的路由定义。但是,两者并不完全相同。其中相同的地方是:params和pathname。useMatch()是主观输入为匹配对象的单数输出。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经验文章,请参考: