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 --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.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
经验文章,请参考:
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。