React-router-dom 6总结
路由传参的几种方式
-
Search传参,类似于url中的 ?xxx=yyy&zzz=rrr 我们一般需要用到 qs 库,用于处理查询 import qs from 'qs';
const detail = qs.parse(search.substring(1));
const str = qs.stringify(detail)
-
Params 传参,需要在路由声明接受参数 类似于restful,/:id/:name/:gender -
state传参 //v5 中的使用方式 to 不再是一个路由字符串,而是一个对象
{/* 方式3:通过state传递参数,在location.state中获取,link中的to传递为一个对象,必须包含pathname与state字段属性 */}
<Link to={{ pathname: '/home/message/detail', state: message }}>{message.title}</Link>
V6的注意点
-
移除了
标签,使用标签替代
-
引入了useRoutes([])用于根据路由表创建根路由,其返回值是dom,用{}包裹,放置于节点下即可配置全部一级路由 -
路由表的创建模式
import News from "../pages/Home/News";
import Message from "../pages/Home/Message";
import { Navigate } from "react-router-dom";
import Detail from "../pages/Home/Message/Detail";
import { lazy } from "react";
const About = lazy(() => import("../pages/About"));
const Home = lazy(() => import("../pages/Home"));
export const routes = [
{ path: "/", element: <Navigate to={"/a/about"} /> },
{ path: "/a/about", element: <About /> },
{
path: "/home",
element: <Home />,
children: [
{ path: "", element: <Navigate to={"news"} /> },
{ path: "news", element: <News /> },
{
path: "message",
element: <Message />,
children: [
{
path: "detail",
element: <Detail />,
},
],
},
],
},
];
-
子组件不需要再使用钩子创建路由,路由框架通过识别子组件的 以及路由表的children关系,可以知道往什么地方渲染子组件,这一点非常方便 -
标签取消了 activeClassName ,className可以赋值一个函数,系统会为该函数传递一个{isActive:true} 的对象作为参数; -
在@5中的几个钩子大多数废弃,例如:useHistory 、useRouteMatch() 已经废弃或移除,useLocation 保留,新增加了useParams ,非常好用,state传参与params传参都有简化 -
我们无法再获取useHistory 钩子再获取history对象,但是可以使用useNavigate 钩子获取navigate 对象,在使用navigate对象导航时也与history不同: const nav = useNavigate()
nav(`/home/message/detail`, { replace: true, state: message })
nav(-1)
nav(1)
nav函数的参数二接受的是一个固定类型的对象,不要随便传(TS在工程化的必要性?) -
我们依旧可以使用React提供的lazy函数,只不过现在需要在路由表中声明
import News from "../pages/Home/News";
import Message from "../pages/Home/Message";
import { Navigate } from "react-router-dom";
import Detail from "../pages/Home/Message/Detail";
import { lazy } from "react";
const About = lazy(() => import("../pages/About"));
const Home = lazy(() => import("../pages/Home"));
export const routes = [
{ path: "/", element: <Navigate to={"/a/about"} /> },
{ path: "/a/about", element: <About /> },
{
path: "/home",
element: <Home />,
children: [
{ index: true, element: <Navigate to={"news"} /> },
{ path: "news", element: <News /> },
{
path: "message",
element: <Message />,
children: [
{
path: "detail",
element: <Detail />,
},
],
},
],
},
];
Tips:不要忘记在根路由组件中使用<Suspense fallback={<h2>Loading</h2>}>{elements}</Suspense> 包裹住根路由表。 -
新增加 end 关键字,当在上一级路由的link标签中使用end关键字,当他的下级路由被点击,他将失去激活状态。 -
路由鉴权,使用高阶组件包一下需要鉴权的页面,如果不能鉴权通过,则跳转,能鉴权通过则使用渲染: import React from 'react'
import { Fragment } from 'react';
import { Navigate } from 'react-router-dom';
import useLocalStorage from '../hooks/useLocalStorage'
//一个简单的鉴权操作
export const AuthWrapComponent = ({ children }) => {
const [token, _] = useLocalStorage('token');
return (
<Fragment>
{/* 注意一定要replace ,导航到404空白页 或者 401无权*/}
{token ? children : <Navigate to='/a/about' replace />}
</Fragment>
)
}
几个钩子的使用
- useLocation: 获取当前路径,state传参时可以拿到 state 对象,还可以通过 patchname 拿到当前路由路径(不包含?)
- useSearchParams:获取当前查询路径对象,需要使用get获取对应key的值
- useParams:拿到 params传参的对象
简单的一个示例: const Detail = (props) => {
// 方式1:通过search传递参数,比较复杂,不推荐,
//需要用解构赋值获得search对象,然后调用get函数获取对应的key的值
// const [search] = useSearchParams()
// const detail = { id: search.get('id'), title: search.get('title') }
// 方式2:通过params参数传递,非常简单直接使用useParams()
// const detail = useParams()
// 方式3:通过state传递参数
// const { state: detail } = useLocation();
// console.log("此处可以收到来自route的state参数:", detail);
//方式1+plus:自定义的钩子根据传入的key数组拿到search中的数据并包装成对象
const detail = useUrlQueryParam(['id', 'title'])
console.log("获取到对象:", detail);
return (
<ul>
<li>ID:{detail.id}</li>
<li>TITLE:{detail.title}</li>
<li>CONTENT:{detail.title}+{detail.id}</li>
</ul>
)
}
export default Detail
//js下极简的封装
export const useUrlQueryParam = (keys) => {
const [search] = useSearchParams()
//遍历keys,从search中获取对应的值,返回一个新对象
const query = keys.reduce((acc, key) => {
acc[key] = search.get(key)
return acc
}, {})
return query
}
例如这样一个URL :http://localhost:3000/projects/1/kanban?id=1 在使用两个钩子的效果: const { pathname } = useLocation()
const [urlparam] = useSearchParams()
console.log(pathname, urlparam);
//### : /projects/1/kanban id=1
可以看出他们是术业有专攻的 在 TypeScript 下封装 useSearchParams 由于 useSearchParams 钩子的不易用,不能直接返回我们需要的对象,我们可以自行封装如下钩子,帮助我们获取对象: import { useMemo, useState } from "react"
import { URLSearchParamsInit, useSearchParams } from "react-router-dom"
export const isVoid = (value: unknown) => value === undefined || value === null || value === "";
export const isNullOrUndefined = (value: unknown) => value === undefined || value === null;
export const cleanObject = (obj?: { [key: string]: unknown }) => {
if (!obj) return {};
const result = { ...obj };
Object.keys(result).forEach((key) => {
const value = result[key];
if (isVoid(value)) {
delete result[key];
}
});
return result;
};
export const subset = <O extends { [key in string]: unknown }, K extends keyof O>(obj: O, keys: K[]) => {
const filteredEntries = Object.entries(obj).filter(([key]) =>
keys.includes(key as K)
);
return Object.fromEntries(filteredEntries) as Pick<O, K>;
};
export const useUrlQueryParam = <K extends string>(keys: K[]) => {
const [searchParams] = useSearchParams()
const setSearchParams = useSetUrlSearchParam()
const [stateKeys] = useState(keys)
return [
useMemo(
() => subset(Object.fromEntries(searchParams), stateKeys) as { [key in K]: string },
[searchParams, stateKeys]
),
(params: Partial<{ [key in K]: unknown }>) => setSearchParams(params)
] as const
};
export const useSetUrlSearchParam = () => {
const [searchParam, setSearchParam] = useSearchParams()
return (params: Partial<{ [key in string]: unknown }>) => {
const o = cleanObject({
...Object.fromEntries(searchParam),
...params
}) as URLSearchParamsInit
return setSearchParam(o)
}
};
----------
const [param, setParam] = useUrlQueryParam(['name', 'personId'])
|