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 替换。
// v5
import { useHistory } from 'react-router-dom';
const history = useHistory();
  
history.push('/tns', data);
history.replace('/tns');
history.goBack();
history.goForward();
history.go(2)
// 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 代替

// 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 获取。
// 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 替换。
// 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 即可。
// 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 为重定向的地址进行跳转。
// 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 声明时无需在起始处写 '/'。
// 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 一致。
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;
// 使用 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 代替。
// 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 参数
import { useSearchParams } from 'react-router';
  
// searchParams 获取参数
// setSearchParams 变更参数
const [searchParams, setSearchParams] = useSearchParams();
  
// 获取参数 id
const id = searchParams.get('id');
  
// 传入 setSearchParams 变更 searchParams
setSearchParams(setSearchParams);
  • Link 组件将 to 传参拆出,且 path 支持相对路径的传递。
// 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 传递函数代替。
// 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 替换方式了,如果你有一些不理解的地方可以积极评论😀。

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

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