react-router v5 升级 v6

⛄:最近在给项目从 react-router v5 升级到 v6,发现 v6 是一次重大更新,有很多的破坏性变更,我这里总结了一些常见的 API 替换方案,希望能帮到大家。

官方升级文档:https://reactrouter.com/en/main/upgrading/v5

升级 react-router 主要有以下收益

  • react-router 包体积减小,20kb -> 8kb。

  • 更小成本的使用 Error Boundaries,可在配置式路由中使用配置的方式在任意子路由接入 Error Boundaries。防止页面白屏,降低白屏率。

  • 配置化路由,路由编写形式更加简洁,使用配置的方式一键生成路由。

  • 更多的 hooks 替代原先的 props。

  • 搭配 Remix 可以有更多的特性

    • 并行懒加载,在深层路由嵌套中,react-router v6 会并行请求所有子路由的资源,而不是像 v5 一样一级一级请求。提高加载速度,路由层级越深效果越明显。 升级步骤

升级步骤

  • 执行命令 npm uninstall @types/react-router,v6 版本使用 ts 重构,无需 ts 类型包。
  • 执行命令 npm update react-router@6 react-router-dom@6,升级 react-router 版本。
  • 更换 API,可直接运行项目查看报错,依据报错位置一步步替换。下文列出了对应 API 的变更及替换方式,大家直接对照自己项目中旧的 API 在文中搜索即可😀。

@types/react-router-dom 升级为相同步骤。

API 变更

useHistory 使用 useNavigate 代替

  • v6 中 useHistory 被移除,不再支持获取 histoy 对象,所有 history 对象上的属性及方法都需要替换。
  • 常用的跳转方法需要使用 useNavigate 替换。
1
2
3
4
5
6
7
8
9
// v5
import { useHistory } from 'react-router-dom';
const history = useHistory();

history.push('/tns', data);
history.replace('/tns');
history.goBack();
history.goForward();
history.go(2)
1
2
3
4
5
6
7
8
9
// v6
import { useNavigate } from 'react-router-dom';
const navigate = useNavigate();

navigate('/tns', { state: data });
navigate('/tns', { replace: true });
navigate(-1);
navigate(1);
navigate(2)

history.location 使用 useLocation 代替

1
2
3
4
5
6
7
8
// v5
import { useHistory } from 'react-router';
const history = useHistory();
const location = history.location;

// v6
import { useLocation } from 'react-router';
const location = useLocation();

history.listen 使用 useEffect 监听 location 代替

  • history.listen 中常用两个参数 location 与 action,分别可以使用 useLocation 与 useNavigationType 获取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// v5
import { useHistory } from 'react-router';
const history = useHistory();
history.listen((location, action) => {
// ...
});

// v6
import { useLocation, useNavigationType } from 'react-router-dom';
const location = useLocation();
const navigationType = useNavigationType();

useEffect(()=>{
// ...
}, [location, navigationType])

history.blocker 使用 useBlocker 代替

  • history.blocker 统一使用 useBlocker 替换,useBlocker 监听函数中,返回 true 阻止跳转,返回 false 不阻止跳转。
  • history.block 中用到的 location 与 action 参数使用 nextLocation historyAction 替换。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// v5
import { useHistory } from 'react-router';
const history = useHistory();
history.block((location , action) => {
// ...
})

// v6
import { useBlocker } from 'react-router';
useBlocker(({ nextLocation, historyAction }) => {
if (xxx) {
return true; // 返回 true 阻止当前跳转
}
return false; // 返回 false 不进行阻止跳转
});

withRouter

  • withRouter 在 v6 中移除。withRouter 主要是向组件中注入 history、location、match 这三个 props,需要观察被观察的组件是否用到了这三个 props,如果用到需要使用新的 hooks,分别使用 useNavigate、useLocation、useMatch 代替,最后删除 withRouter 即可。

Switch 替换为 Routes

  • v6 中 Switch 被移除,直接替换为 Routes 即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// v5
import { Route, Switch } from 'react-router-dom';
<Switch>
<Route path="xxx">
<XXX />
</Route>
</Switch>

// v6
import { Route, Routes } from 'react-router-dom';
<Routes>
<Route path="xxx">
<XXX />
</Route>
</Routes>

Redirect 替换为 Navigate

  • Redirect v6 中被移除,使用 Navigate 传入 to 为重定向的地址进行跳转。
1
2
3
4
5
// v5
<Redirect from={routePath} to={redirectPath} exact />

// v6
<Navigate to={redirectPath} />

Route 组件变更

  • strict 被删除,path 末尾的 ‘/‘ 默认被忽略。
  • exact 被删除,深度匹配在 path 末尾加 ‘/*’ 开启。
  • sensitive 大小写敏感替换为 caseSensitive。
  • children 中不再需要内容,替换为 element 用于传递子路由组件。
  • path:react-router v6 中路径是相对的,path 声明时无需在起始处写 ‘/‘。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// v5
<Route
key={routePath}
path={routePath}
sensitive={sensitive} // 大小写是否敏感替换为 caseSensitive
strict={strict} // strict 被删除了 末尾的'/'会被忽略
exact={exact} // exact 被删除了 需要进行深度匹配在末尾加* path/*
>
{children}
</Route>

// v6
<Route
key={routePath}
path={routePath}
caseSensitive={caseSensitive}
element={children}
/>

RouteObject 配置式路由

  • v5 版本仅能使用 jsx 进行路由的编写,在 v6 版本中可以升级为配置式路由。
  • 父级路由中需要在内部使用 <Outlet /> 组件,用于挂载子路由的入口。
  • 路由配置变更与 Route 一致。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { RouteObject } from 'react-router';
const routeConfig: RouteObject[] = [
{
path: '/',
Component: XXX, // 该路由下挂载的组件
children: [
{
path: 'xxx',
ErrorBoundary: XXX, // 异常捕获组件
children: [
{
path: 'xxx/*',
// 懒加载,需要文件中导出 Component 命名的组件
lazy: () => import('xxx'),
},
],
},
{
path: '*',
Component: NotFound,
},
],
},
];

export default routeConfig;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 使用 createBrowserRouter 与 RouterProvider 消费配置式路由
import {
createBrowserRouter,
RouterProvider,
} from "react-router-dom";

import routeConfig from 'xxx';

import Root, { rootLoader } from "./routes/root";
import Team, { teamLoader } from "./routes/team";

const router = createBrowserRouter(routeConfig);

ReactDOM.createRoot(document.getElementById("root")).render(
<RouterProvider router={router} />
);

useRouteMatch 使用 useMatch useMatchs 代替

  • useRouteMatch 被移除,可使用 useMatch useMatchs 代替。
1
2
3
4
5
6
7
8
9
10
11
// v5
import { useRouteMatch } from 'react-router';
// 获取到当前的路由匹配的 match 对象
const match = useRouteMatch();

// v6
import { useMatch, useMatches } from 'react-router';
// 单独获取 match 对象需要传入 path
const match = useMatch('/example');
// 获取从根路由到当前路由的全部 matches 对象
const matches = useMatches();

使用 useSearchParams 获取路由参数

  • v6 中新增 hooks 用于获取/设置 url 上的 query 参数
1
2
3
4
5
6
7
8
9
10
11
import { useSearchParams } from 'react-router';

// searchParams 获取参数
// setSearchParams 变更参数
const [searchParams, setSearchParams] = useSearchParams();

// 获取参数 id
const id = searchParams.get('id');

// 传入 setSearchParams 变更 searchParams
setSearchParams(setSearchParams);
  • Link 组件将 to 传参拆出,且 path 支持相对路径的传递。
1
2
3
4
5
6
7
8
9
10
11
12
// v5
import { Link } from "react-router-dom";
<Link to={{ pathname: "/home", state: state }} />

// v6
import { Link } from "react-router-dom";
<Link to="/home" state={state} />

// 支持相对 path 的形式
<Link to="..">All Users</Link>
<Link to=".">User Profile</Link>
<Link to="../mj">MJ</Link>
  • 原先的 exact 参数替换为 end。
  • activeClassName 与 activeStyle 被删除,可以向 NavLinke className 与 style 传递函数代替。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// v5
<NavLink
to="/messages"
style={{ color: 'blue' }}
activeStyle={{ color: 'green' }}
className="nav-link"
activeClassName="activated"
>
Messages
</NavLink>

// v6
<NavLink
to="/messages"
style={({ isActive }) => ({ color: isActive ? 'green' : 'blue' })}
className={({ isActive }) => 'nav-link' + (isActive ? 'activated' : '')}
>
Messages
</NavLink>

结语

以上就是 react-router v5 升级 v6 时常见的 API 替换方式了,如果你有一些不理解的地方可以积极评论😀。

如果对你有帮助的话记得帮我点个赞 👍。

文章内容有不正确的地方请指出,我会及时更改 ⛄。