Skip to main content

从v5升级

向后兼容包

我们正在积极开发一个向后兼容方案, 该方案在 v6 的基础上实现 v5 API, 主要是为了升级尽可能顺利。如果升级到 v6, 只需要对应的代码进行最小的变更, 然后, 你可以逐渐的将代码迭代到 v6 的 API 。

危险

虽然升级项目有很多方案, 但还是建议等待我们的向后兼容包

现在, 我们希望本指南将帮助您一次性完成升级!

介绍

React 路由 v6 版本引入了一些强大的新功能, 以及对最新的 React 的兼容与改进, 它还从 v5 版本引入了一些重大的更改。本文档是有关于如何将你的 v4/5 项目升级到 v6 的综合指南, 同时希望能尽可能多地发布。

如果您刚刚开始使用 React Router, 或者您想在新项目中试用 v6, 请参考 入门指南

在指南的示例里, 将显示如何在V5项目中生成某些内容的代码示例, 以及如何在 v6 中完成相同的操作。还将解释我们进行此更改的原因, 以及它将如何改进您的代码以及使用您的应用的用户的整体用户体验。

通常, 该过程如下所示:

  1. 升级到 React v16.8 或更高版本
  2. 升级到 React Router v5.1
  3. 升级到 React Router v6

以下是每个步骤的详细分解, 可帮助您快速、自信地迁移到 v6。

升级到 React v16.8 或更高版本

React Router v6 大量使用了React hooks, 因此在尝试升级到 React Router v6 之前,需要使用React 16.8 及更高的版本。还好 React Router v5 与 React >= 15 是兼容的, 所以如果你使用的是 v5 (或者 v4 ), 你应该能够在不接触任何路由器代码的情况下升级 React。

升级到 React 16.8 之后, 你可以部署你的项目 你可以回头看下你之前的路由

升级到 React Router v5.1

如果你先升级到 v5.1, 在切换到 React Router v6 将会更容易, 在 v5.1 中, 我们发布了对 <Route children> 元素处理的增强功能, 这些功能将有助于顺利度过到 v6。无需使用 <Route component> and <Route render> 属性, 只需在任意位置使用常规元素 <Route children> , 并且使用 hooks 访问路由器的内部状态。

// v4 和 v5 之前的版本 5.1
function User({ id }) {
// ...
}

function App() {
return (
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route
path="/users/:id"
render={({ match }) => (
<User id={match.params.id} />
)}
/>
</Switch>
);
}

// v5.1 首选样式
function User() {
let { id } = useParams();
// ...
}

function App() {
return (
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
{/* 也可以使用`children`属性 */}
<Route path="/users/:id" children={<User />} />
</Switch>
);
}

你可以阅读更多关于 v5.1 钩子的 API 和 迁移常规元素后面的更多信息 我们的博客.

通常, React Router v5.1(和 v6)更喜欢元素而不是组件(或"元素类型")。这有几个原因, 但是当我们讨论 v6 的 <Route> PI 时, 我们将进一步讨论。

当你使用常规的 React 元素时, 你可以传递属性, 这有助于提高代码的可读性并随着时间的推移进行维护。如果你使用 <Route render> 获取参数, 则可以在路由组件中使用useParams

随着升级到 v5.1, 你可以用钩子替换 withRouter 的任何用法, 你也可以去掉不在 <Switch> 内的存在 <Route> 元素。 同样关于 v5.1 的博客文章详细说明如何执行此操作。

总之, 要从 v4/5 升级到 v5.1, 你应该:

  • 使用 <Route children> 替换 <Route render><Route component> 属性
  • 使用 我们的钩子 API 到访问路由的状态, 如当前位子和参数
  • 用钩子 替代 withRouter 的所有用法
  • 不在 <Switch> 内的任何 <Route> 替换为 useRouteMatch 或者包装成一个 <Switch>

移除 <Switch> 内的 <Redirect>

移除 <Switch> 内的所有 <Redirect> 元素

如果你要在初始渲染时进行重定向, 则应该将重定向逻辑迁移到服务断(我们 在此处对此进行了详细介绍).

如果要重定向客户端, 请将 <Redirect> 移动到 <Route render> 属性内

// 改变这里:
<Switch>
<Redirect from="about" to="about-us" />
</Switch>

// 到:
<Switch>
<Route path="about" render={() => <Redirect to="about-us" />} />
</Switch>

不在 <Switch> 内的正常 <Redirect> 元素可以保留, 在 v6 中统一用 <Navigate> 元素处理。

重构自定义 <Route>

用常规 <Route> 替换 <Switch> 内任何不是普通 <Route> 元素的元素。这包括任何 <PrivateRoute> 样式的自定义组件。

你可以在此处阅读有关此方法背后的基本原理的更多信息, 包括有关于如何在v5中使用 <Route render> 属性实现相同效果的一些提示。

切记!

同样, 将项目升级到 v5.1 后, 应对其进行测试和部署, 如果在以前项目中继续开发,可以重新选择本指南。

升级到 React Router v6

注意: 这是迁移过程中最大的一步, 可能需要花费最多的时间和精力。

对于此步骤, 你需要安装 React Router v6。如果你通过 npm 管理依赖:

$ npm install react-router-dom
# 或者, 对于 React Native 应用程序
$ npm install react-router-native

你还需要在 package.json 中删除 history 依赖项。 history 库是 v6 的直接依赖项(不是对等的依赖), 所以你永远不会直接导入或使用它。相反, 您将对所有导航使用 useNavigate() 钩子进行所有的导航(请参见下文)

将所有的 <Switch> 元素升级到 <Routes>

React Router v6 引用了一个类似 SwitchRoutes 组件,但功能更强大, Routes 相比 Switch 的主要优势是:

  • <Routes> 内的所有 <Route><Link> 都是相对的, 这导致了在 <Route path><Link to> 内更精简、更可维护的代码

  • 路由是根据最佳匹配选择的,而不是按照顺序遍历。这避免了由于不可访问的路由导致错误, 因为它们没有定义到 <Switch>

  • 路由可以嵌套在一个位置, 而不是分散在不同组件的中。 在中小型项目中, 这可以让你一次轻松查看所有的路由。在大型项目中, 你仍然可以将路由嵌套在通过 React.lazy 动态加载的捆绑包中

要使用 v6, 你需要将所有的 <Switch> 元素替换成 <Routes> 。如果你已经升级到 v5.1, 那么您已经完成了一半。

首先, 让我们谈谈 v6 的相对 routes 和 links

在 v5 中, 你必须非常明确地说明你希望如何嵌套 routes 和 links。在这两种情况下, 如果想要嵌套的 routes 和 links, 则必须从父路由的 match.url 和 match.path 值, 构建 <Route path><Link to> 的属性, 此外, 如果要嵌套路由, 就必须将他们放到子路由的组件中。

// 这是一个 React Router v5 应用
import {
BrowserRouter,
Switch,
Route,
Link,
useRouteMatch
} from "react-router-dom";

function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/users">
<Users />
</Route>
</Switch>
</BrowserRouter>
);
}

function Users() {
// 在v5中, 嵌套路由子组件渲染, 因此
// 你项目中都要<Switch> 元素来嵌套UI
// 你构建嵌套 routes 和 links 使用 match.url 和 match.path
let match = useRouteMatch();

return (
<div>
<nav>
<Link to={`${match.url}/me`}>My Profile</Link>
</nav>

<Switch>
<Route path={`${match.path}/me`}>
<OwnUserProfile />
</Route>
<Route path={`${match.path}/:id`}>
<UserProfile />
</Route>
</Switch>
</div>
);
}

这与v6中对应的项目:

// 这是 一个 Reacr Router v6 例子
import {
BrowserRouter,
Routes,
Route,
Link
} from "react-router-dom";

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users/*" element={<Users />} />
</Routes>
</BrowserRouter>
);
}

function Users() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>

<Routes>
<Route path=":id" element={<UserProfile />} />
<Route path="me" element={<OwnUserProfile />} />
</Routes>
</div>
);
}

在此示例中, 有关 v6 需要注意的一些重要事项:

  • <Route path><Link to> 都是相对的。这意味着他们自动建立在父路的 path 和 URL 上, 因此你不必手动插match.url 或者 match.path

  • <Route exact> 取消了, 相反, 具有子路由(在其他组件中定义)的路由在其路径中使用尾随"*"来深度匹配

  • 你可以把你的 routes 按照你希望的顺序, router 也将自动检测当前 URL 的最合适路由, 这可以防止在 <Switch> 中通过手动设置路由顺序排列导致的错误

您可能还注意到, v5 项目中的所有 <Route children> 到 v6 时候 都变成 <Route element> 。假如你遵循升级到 v5.1 的步骤, 这应该就像将 route 元素从子位置移动到命名 element 属性 一样简单。

<Route element> 的优点

在有关升级到 v5.1 的部分中, 我们承诺将讨论使用常规元素而不是组件(或元素类型)进行渲染的优势, 让我们从升级中快速调整一下, 现在谈谈这一点。

对于初学者, 我们看 React 本身在这里以 <Suspense fallback={<Spinner />}> API 中处于领先地位, fallback 属性采用的是 React 元素,而不是组件。这使您可以轻松地将所需的任何属性传递给 <Spinner> 从渲染它的组件。

使用元素而不是组件意味着我们不必提供passProps样式的 API, 这样你就可以获得元素所需的 props。例如, 在基于组件的 API 中, 没有一种好方法可以将 props 传递给 <Route path=":userId" 组件={Profile} /> 匹配时渲染的 <Profile> 元素。大多数采用这种方法的 React 库最终都会使用 API, 如 <Route component={Profile} passProps={{ animate: true }} /> 或者使用 render prop 或更高阶组件。

另外, 如果你没有注意到, 在 v4 和 v5 中, Route 渲染 API 变得相当大。它像这样:

// 嗯, 这个比较简单!
<Route path=":userId" component={Profile} />

// 等等, 如何将自定义属性传递给<Profile> 组件??
// 嗯~, 也许我可以在这种情况下使用渲染props?
<Route
path=":userId"
render={routeProps => (
<Profile routeProps={routeProps} animate={true} />
)}
/>

// 好的, 现在我们有两种方式来渲染路径. :/

// 但是 等等, 如果我们想在创建路由时渲染某些内容
// *没有* 匹配 URL , 比如找不到页面? 也许我们
// 可以使用另一个语义稍有不同的渲染属性?
<Route
path=":userId"
children={({ match }) => (
match ? (
<Profile match={match} animate={true} />
) : (
<NotFound />
)
)}
/>

// 如果我想访问路线匹配, 或者我需要
// 如何重定向更深的DOM层级?
function DeepComponent(routeStuff) {
// 有路线图, 呸
}
export default withRouter(DeepComponent);

// 好吧, 嘿, 现在至少我们已经涵盖了所有的用例!
// ... *facepalm*

这些API杂乱的部分原因是 React 没有为我们提供任何方法来获取来自 <Route> 元素的信息,所以我们不得不发明聪明的方法来将 route 数据 你自己定义的属性传递到你的元素:component, render props , passProps 高阶函数... 直到 hooks 出现!

现在, 上面的表述是这样的:

// 啊, 很简单的API。它就像<suspence>API!
// 这里没有多的可以说的.
<Route path=":userId" element={<Profile />} />

// 但是等等, 我如何将自定义属性传递到<Profile>
// 只有一个元素的时候容易
<Route path=":userId" element={<Profile animate={true} />} />

// 好的, 但是我如何访问路由器的数据, 比如URL参数
// 还是当前的location?
function Profile({ animate }) {
let params = useParams();
let location = useLocation();
}

// 但是对于多级组件的情况了?
function DeepComponent() {
// 哦, 对了, 和其他地方一样
let navigate = useNavigate();
}

// 嗯嗯额 到此为止.

在 v6 中使用element属性的另一个重要原因是, <Route children> 是为了嵌套路由而存在, 这是人们最喜欢的 v3 和 @reach/router 的功能之一, 我们将在 v6 中将其带回。

将上一个示例中的代码更进一步, 我们可以将所有 <Route> 元素提升到单个路由配置中:

// 这是一个 React Router v6 应用
import {
BrowserRouter,
Routes,
Route,
Link,
Outlet
} from "react-router-dom";

function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="users" element={<Users />}>
<Route path="me" element={<OwnUserProfile />} />
<Route path=":id" element={<UserProfile />} />
</Route>
</Routes>
</BrowserRouter>
);
}

function Users() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>

<Outlet />
</div>
);
}

当然, 此步骤是可选的, 但对于没有数千个路由的中小型项目来说, 它真的很好。

注意, <Route> 元素如何顺利嵌套在一个 <Routes> 元素中。嵌套路由通过添加到父路由的路径来构建其路径。这次我们不需要在 <Route path="users"> 尾部上添加一个*,因为当 routes 定义时, router 能看到所有嵌套的路由。

只有当路径的后代元素中有另一个 <Routes> 时, 你才需要在尾部添加*, 在这种情况下, 后代 <Routes> 将在保留的路径名部分上进行匹配(请参见前面的示例, 了解实际情况)。

使用嵌套配置时, 带有children的路由应呈现一个 <Outlet> , 以便呈现其子路由。这使得使用嵌套UI呈现布局变得很容易。

关于 <Route path> 模式的说明

React Router v6 使用简化的路径格式。 <Route path> 在 v6 中支持2种站位符: 动态:id样式参数和*通配符。*通配符只能在路径末尾使用, 而不能在中间。

以下所有内容都是 v6 中的有效路由路径:

/groups
/groups/admin
/users/:id
/users/:id/messages
/files/*
/files/:id/*

以下正则表达式样式的路由路径在 v6 中 无效:

/users/:id?
/tweets/:id(\d+)
/files/*/cat.jpg
/files-*

我们在 v4 中添加了对路径到正则表达式的依赖关系, 以支持更高级的模式匹配。在 v6 中, 我们使用了一种更简单的语法, 它允许我们可以预测的方式解析路径以进行排名。这也意味着我们可以停止依赖正则的路径,这对于包的大小来说很友好。

如果您使用的是 path-to-regexp 的任何更高级的语法, 则必须将其删除并简化路由路径。如果你使用 RegExp 语法进行 URL 参数验证(例如, 确保 id 都是数字字符), 请注意, 我们计划在某个时候在 v6 中添加一些更高级的参数验证。现在, 您需要将该逻辑移动到路由渲染的组件, 并在解析参数后让它分支进行渲染。

如果你使用的是 <Route sensitive> , 你应该将其移动到包含 <Routes caseSensitive> 属性中, <Routes> 元素中的所有路由要么区分大小写, 要么不区分大小写。

需要注意的另一件事是, v6 中的所有路径匹配都会忽略 URL 上的末尾斜杠。实际上, <Route strict> 已被删除, 在 v6 中不起作用。这并不意味着如果需要, 不能使用尾随斜杠。你的项目可以决定是否使用末尾斜杠, 你只是不能在 <Route path="edit"><Route path="edit/"> 渲染两个不同的 UI 客户端。你仍然可以在这些URL上渲染两个不同的UI(尽管我们不建议这样做), 但你必须在服务器端执行此操作。

在 v5 中, 一个不以/开头的 <Link to> 值是不明确的; 这取决于当前URL是什么。例如, 如果当前 URL 为/users/,则 v5 中用 <Link to="me"> 将渲染 <a href="/me"> 。但是, 如果当前 URL 后面有一个斜杠,如/users/,则相同的 <Link to="me"> 将渲染成 <a href="/users/me"> 。这使得很链接的行为难预测,因为在 v5 中, 我们建议您从根 URL 构建链接 (使用match.url), 而不要使用相对的 <Link to>

React Router v6 修复了这种模糊性, 在 v6 中, 一个 <Link to="me"> 将始终呈现相同的 <a href> ,不管当前的 URL 是什么。

例如, 在 <Route path="users"> 中渲染 <Link to="me"> 将渲染始终指向/users/me的link,而不管当前 URL 是否有尾部斜杠。

当你想通过 link 向上返回到父级路由。使用..到你的 <Link to> 的值中, 类似你在 <a href> 中所做的操作。

如果要将"向上"链接回父路由, 请使用前导 ..<链接到>值中的段, 类似于您在 <href> 中执行的操作。

function App() {
return (
<Routes>
<Route path="users" element={<Users />}>
<Route path=":id" element={<UserProfile />} />
</Route>
</Routes>
);
}

function Users() {
return (
<div>
<h2>
{/* 这个 links 指向 /users - 当前路由 */}
<Link to=".">Users</Link>
</h2>

<ul>
{users.map(user => (
<li>
{/* 这个 links 指向 /users/:id - 子路由 */}
<Link to={user.id}>{user.name}</Link>
</li>
))}
</ul>
</div>
);
}

function UserProfile() {
return (
<div>
<h2>
{/* 这个 links 指向 /users - 父路由 */}
<Link to="..">All Users</Link>
</h2>

<h2>
{/* 这个 links 指向 /users/:id - 当前路由*/}
<Link to=".">User Profile</Link>
</h2>

<h2>
{/* 这个 links 指向 /users/mj - 同级路由 */}
<Link to="../mj">MJ</Link>
</h2>
</div>
);
}

将当前 URL 视为文件系统上的目录路径, <Link to> 类似 cd 命令行实用程序, 这可能会有所帮助。

// 如果你的路由是这样的
<Route path="app">
<Route path="dashboard">
<Route path="stats" />
</Route>
</Route>

// 当前URL是 /app/dashboard (或者尾部不带斜杆)
<Link to="stats"> => <a href="/app/dashboard/stats">
<Link to="../stats"> => <a href="/app/stats">
<Link to="../../stats"> => <a href="/stats">
<Link to="../../../stats"> => <a href="/stats">

// 在命令行上, 如果当前目录为/app/dashboard
cd stats # pwd is /app/dashboard/stats
cd ../stats # pwd is /app/stats
cd ../../stats # pwd is /stats
cd ../../../stats # pwd is /stats

注意: 我们的团队决定在匹配和创建相对路径时忽略尾部斜杠。我们咨询了一些朋友和客户(他们也是我们的朋友!)。我们发现, 我们大多数人甚至不明白如何使用尾部斜杠处理普通的HTML相对链接。大多数人猜测它在命令行上像 cd 一样工作(它没有)。此外, HTML相对链接没有嵌套路由的概念, 它们只在 URL 上工作, 所以我们不得不在这里开辟自己的道路。@reach/router 器开创了这个先例, 并且已经运行了几年。

除了忽略当前 URL 中的尾随斜杠之外, 请务必注意, <Link to=".."><Route path> 与 URL 的多个段匹配时的行为并不总是像 <a href=".."> 。而不是只删除 URL 的一个部分。它将根据父路由的路径进行解析, 实质上是删除该路由指定的所有路径段

function App() {
return (
<Routes>
<Route path="users">
<Route
path=":id/messages"
element={
// This links to /users
<Link to=".." />
}
/>
</Route>
</Routes>
);
}

这似乎是一个奇怪的选择, 做出..在 routes 而不是 URL 段上运行, 但是在使用路由时, 这是一个巨大的帮助, 其中不确定数量的段可能与匹配。在这些情况下, 单个 .. <Link to> 值中的段基本上可以删除与 * 匹配的任何内容, 这使您可以在 * 路由中创建更可预测的 links。

function App() {
return (
<Routes>
<Route path=":userId">
<Route path="messages" element={<UserMessages />} />
<Route
path="files/*"
element={
// 这是 /:userId/messages,
// 通过 * 无论有多少部分
<Link to="../messages" />
}
/>
</Route>
</Routes>
);
}

使用 useRoutes 而不是 react-router-config

v5 的 react-router-config 包中的所有功能都已转移到 v6 的核心。如你喜欢/需要 将路由定义为 JavaScript 对象, 而不是 React 元素, 那么你一定喜欢这个。

function App() {
let element = useRoutes([
// 这与你<Route> 提供相同的属性
{ path: "/", element: <Home /> },
{ path: "dashboard", element: <Dashboard /> },
{
path: "invoices",
element: <Invoices />,
// 嵌套路由创建子属性,
// 这属性与<Route>也是一样的
children: [
{ path: ":id", element: <Invoice /> },
{ path: "sent", element: <SentInvoices /> }
]
},
// 没有匹配到路由的情况
{ path: "*", element: <NotFound /> }
]);

// 返回的元素将呈现整个元素
// hierarchy with all the appropriate context it needs
return element;
}

以这种方式定义的路由同 <Routes> 语义, 事实上, <Routes> 实际上只是围绕 useRoutes 的包装。

我们鼓励您同时使用 <Routes>useRoutes, 并自行决定您更喜欢使用哪一个。老实说, 他们两种我都喜欢。

如果您已经围绕数据获取和渲染服务器端制定了一些自己的逻辑, 那么我们也有一个低级的 matchRoutes 函数, 类似于我们在 react-router-config 中拥有的函数。

使用 useNavigate 而不是 useHistory

React Router v6 引入了一个新的导航API, 它与 <Link> 同一个意思, 并提供了 suspense-enabled 用法更好的兼容性, 我们根据你的风格和需求, 包括此 API 的命令性和声明性版本。

// 这是React Router v5 应用
import { useHistory } from "react-router-dom";

function App() {
let history = useHistory();
function handleClick() {
history.push("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}

在 v6 中, 这个应用使用 navigate API 进行重写。这意味着将 useHistory 更换成 useNavigate, 并且更换 history.pushhistory.replace

// 这是一个 React Router v6 应用
import { useNavigate } from "react-router-dom";

function App() {
let navigate = useNavigate();
function handleClick() {
navigate("/home");
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}

如果需要替换当前位置, 而不是将新位置推送到历史记录堆栈上, 请使用 navigate(to, { replace: true })。如果需要状态, 请使用 navigate(to, { state })。您可以将navigate的第一个参数视为您的 <Link to> , 而将其他参数视为replacestate 的属性。

如果你更喜欢使用声明性 API 进行导航(v5 Redirect 组件), v6 会提供 Navigate 组件。像这样使用它:

import { Navigate } from "react-router-dom";

function App() {
return <Navigate to="/home" replace state={state} />;
}

注意: 请注意v5 <Redirect /> 默认使用 replace 逻辑(你可以通过 push 属性去修改), 另一方面, v6 <Navigate /> 默认使用 push 逻辑, 你可以通过 replace 属性进行更改。

// 改变此项:
<Redirect to="about" />
<Redirect to="home" push />

// 到这:
<Navigate to="about" replace />
<Navigate to="home" />

如果当前正在使用 useHistory 中的 go, goBackgoForward 来回导航, 您还应该将它们替换为 navigate, 并使用数字参数指示指针在历史堆栈中的移动位置。例如, 下面是一些使用 v5 的 useHistory 钩子的代码:

// 这 是React Router v5的应用
import { useHistory } from "react-router-dom";

function App() {
const { go, goBack, goForward } = useHistory();

return (
<>
<button onClick={() => go(-2)}>
Go 2 pages back
</button>
<button onClick={goBack}>Go back</button>
<button onClick={goForward}>Go forward</button>
<button onClick={() => go(2)}>
Go 2 pages forward
</button>
</>
);
}

下面与 v6 相同的应用:

// 这 是React Router v6的应该
import { useNavigate } from "react-router-dom";

function App() {
const navigate = useNavigate();

return (
<>
<button onClick={() => navigate(-2)}>
Go 2 pages back
</button>
<button onClick={() => navigate(-1)}>Go back</button>
<button onClick={() => navigate(1)}>
Go forward
</button>
<button onClick={() => navigate(2)}>
Go 2 pages forward
</button>
</>
);
}

同样,我们从以前的 history API 转向 navigate 的主要原因之一, 为了与 React suspense 更好的兼容。React router v6 在组件层次结构的根目录下使用 useTransition 挂钩。这使我们能够在用户交互需要中断挂起的路由转换时提供更流畅的体验。例如, 当他们单击指向另一个路由的链接时, 而之前单击的链接仍在加载中。navigate API 知道内部挂起的转换状态, 并将执行 REPLACE 而不是 PUSH 到历史记录堆栈, 因此用户不会在其历史记录中最终获得从未实际加载的页面。

注意: v5 中的 <Redirect> 元素不再支持作为路由配置的一部分 (在 <Routes> 内)。这是由于 React 中即将发生的更改, 使得在初始渲染期间更改路由器的状态不安全。如果您需要立即重定向,可以 a) 在服务器上执行重定向(这可能是最好的解决方案)。 也可以 b)在路由组件渲染 <Navigate> 组件。但是, 要认识到导航将发生在useEffect中。

除了不确定性的兼容之外, navigateLink一样, 也支持相对导航, 例如:

// 假设我们在 `/stuff`
function SomeForm() {
let navigate = useNavigate();
return (
<form
onSubmit={async event => {
let newRecord = await saveDataFromForm(
event.target
);
// 你创建自己的URL
navigate(`/stuff/${newRecord.id}`);
// 或者相对导航,就像 Link 一样
navigate(`${newRecord.id}`);
}}
>
{/* ... */}
</form>
);
}

<Link> 不再支持覆盖返回的锚点标记的组件属性, 这里有几个原因。

首先, <Link> 几乎总是渲染 <a> 。如果你的项目中没有, 那么你的项目可能存着一些严重的可访问性和可用性问题, 这没什么好处。浏览器为我们提供了许多具有 <a> 的可用性功能, 我们希望您的用户免费获得这些功能!

话虽如此, 也许你的项目使用了 CSS-in-JS 库, 或者你的设计系统中已经有一个自定义的、花哨的链接组件, 你想用它来渲染。在钩子出现之前的世界里, 组件属性可能已经足够好了, 但现在你可以用我们的几个钩子创建自己的可访问 Link 组件:

import { FancyPantsLink } from "@fancy-pants/design-system";
import {
useHref,
useLinkClickHandler
} from "react-router-dom";

const Link = React.forwardRef(
(
{
onClick,
replace = false,
state,
target,
to,
...rest
},
ref
) => {
let href = useHref(to);
let handleClick = useLinkClickHandler(to, {
replace,
state,
target
});

return (
<FancyPantsLink
{...rest}
href={href}
onClick={event => {
onClick?.(event);
if (!event.defaultPrevented) {
handleClick(event);
}
}}
ref={ref}
target={target}
/>
);
}
);

如果你使用的是 react-router-native , 我们提供了 useLinkPressHandler 与他用法相似。只需要在 Link 属性的 onPress 中调用该钩子返回的函数,就可以了。

这是一个简单属性重命名, 以更好地与 React 生态系统中其他库的常见做法保持一致。

v6.0.0-beta.3 开始, activeClassNameactiveStyle 属性已经从 NavLinkProps 中删除了。相反, 你可以将函数传递给 style 或者 className , 这将允许您根据组件的活动状态自定义内联样式或类字符串。

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

如果你希望保存 v5 属性, 则可以创建自己的 <NavLink /> 作为一个包装组件, 以获得更顺畅的升级路径。

import * as React from "react";
import { NavLink as BaseNavLink } from "react-router-dom";

const NavLink = React.forwardRef(
({ activeClassName, activeStyle, ...props }, ref) => {
return (
<BaseNavLink
ref={ref}
{...props}
className={({ isActive }) =>
[
props.className,
isActive ? activeClassName : null
]
.filter(Boolean)
.join(" ")
}
style={({ isActive }) => ({
...props.style,
...(isActive ? activeStyle : null)
})}
/>
);
}
);

react-router-dom/server 库中获取 StaticRouter

StaticRouter 组件已经挪到 react-router-dom/server 库中了。

// 之前
import { StaticRouter } from "react-router-dom";
// 现在
import { StaticRouter } from "react-router-dom/server";

作出这样的变化是为了更紧密地遵循 react-dom 包里面的约定, 并帮助用户更好地了解 <StaticRouter> 的用途以及何时应该使用它(在服务器上)。

useRouteMatch 替换 useMatch

useMatch 与 v5 的 useRouteMatch非常相似,但有一些关键区别:

  • 它使用我们新匹配路径模式算法
  • 这模式参数现在是必须的
  • 不再接受模式数组
  • 当模型作为对象传递时,一些选项已经重命名, 以便更好地与 v6 中的其他 API 对齐。
    • useRouteMatch({ strict }) 现在是 useMatch({ end })
    • useRouteMatch({ sensitive }) 现在是 useMatch({ caseSensitive })
  • 它返回具有不同形状的匹配对象。 要查看新useMatch钩子的确切API以及其类型声明, 请查看我们的 API 参考.

<Prompt> 当前不支持

当前发布的 v6 版本中不包括 v5 的 <Prompt> (以及来自 v6 Beta 的 usePromptuseBlocker )。我们认为,与其花更多的时间来确认一个还没完全成熟的功能, 还不如用我们现在已完善的功能进行发布。在不久的将来, 我们一定会把他重新添加到 v6 中, 但不会我们第一个稳定版本 6.x。

我们可能会遗漏什么 ?

尽管我们尽力做了很多, 但我们可能也有遗漏。如果你参考我们的指南, 发现有问题。请告知我们, 我们很乐意帮助您了解如何处理 v5 代码, 以便能够升级和利用v6中所有的新东西

祝你好运 🤘