Skip to content

从 v6 升级

如果启用了所有的未来特性标志,那么升级到 v7 时就不会有破坏性的变更。这些标志能让你每次针对一项变更来更新你的应用程序。我们强烈建议你在每完成一个步骤后进行一次提交并发布,而不是一次性完成所有操作。

更新到最新的 v6.x

首先更新到 v6.x 的最新版本,以获取最新的未来特性标志以及控制台警告信息。

👉 更新到最新的 v6 版本。

sh
npm install react-router-dom@6

v7_relativeSplatPath

背景

针对诸如 dashboard/*(就 * 而言)这类多片段通配符路径,改变其相对路径匹配和链接方式。如需了解更多信息,请查看变更日志

👉 启用该特性标志

启用该特性标志取决于路由器的类型:

tsx
<BrowserRouter
  future={{
    v7_relativeSplatPath: true,
  }}
/>
ts
createBrowserRouter(routes, {
  future: {
    v7_relativeSplatPath: true,
  },
});

更新你的代码

如果你有任何带有路径及通配符(例如 <Route path="dashboard/*"> 这样的)的路由,并且其下方存在像 <Link to="relative"><Link to="../relative"> 这样的相对链接,那么你将需要更新你的代码。

👉 <Route> 拆分成两个

将任何包含多片段通配符的 <Route> 拆分成一个带有路径的父路由以及一个带有通配符的子路由:

tsx
<Routes>
  <Route path="/" element={<Home />} />
   <Route path="dashboard/*" element={<Dashboard />} />
   <Route path="dashboard">
     <Route path="*" element={<Dashboard />} />
   </Route>
</Routes>

// or
createBrowserRouter([
  { path: "/", element: <Home /> },
  {
     path: "dashboard/*", 
     element: <Dashboard />, 
     path: "dashboard", 
     children: [{ path: "*", element: <Dashboard /> }], 
  },
]);

👉 更新相对链接

更新该路由树内的任何 <Link> 元素,使其包含额外的“..”相对路径片段,以便能继续链接到同一位置。

tsx
function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
      <nav>
         <Link to="/">Dashboard Home</Link>
         <Link to="team">Team</Link>
         <Link to="projects">Projects</Link>
         <Link to="../">Dashboard Home</Link>
         <Link to="../team">Team</Link>
         <Link to="../projects">Projects</Link>
      </nav>

      <Routes>
        <Route path="/" element={<DashboardHome />} />
        <Route path="team" element={<DashboardTeam />} />
        <Route
          path="projects"
          element={<DashboardProjects />}
        />
      </Routes>
    </div>
  );
}

v7_startTransition

背景

此项功能使用 React.useTransition 而非 React.useState 来进行路由器状态更新。如需了解更多信息,请查看变更日志

👉 启用该特性标志

tsx
<BrowserRouter
  future={{
    v7_startTransition: true,
  }}
/>

// or
<RouterProvider
  future={{
    v7_startTransition: true,
  }}
/>

👉 更新你的代码

除非你在组件内部使用了 React.lazy,否则你无需进行任何更新。

在组件内部使用 React.lazyReact.useTransition(或其他在组件内部创建 promises 的代码)是不兼容的。将 React.lazy 移到模块作用域中,并停止在组件内部创建 promises。这并非是 React Router 的限制,而是 React 的错误用法导致的。

v7_fetcherPersist

提示:

如果你没有使用 <RouterProvider>,则可以跳过此项。

背景

现在,获取器(fetcher)的生命周期基于它何时返回到一个空闲状态,而非其所属组件卸载之时。如需了解更多信息,请查看变更日志

👉 启用该特性标志

ts
createBrowserRouter(routes, {
  future: {
    v7_fetcherPersist: true,
  },
});

更新你的代码

它不太可能会影响到你的应用程序。你可能需要检查一下 useFetchers 的各种使用情况,因为它们的存续时间可能会比之前更长。取决于你正在进行的操作,你渲染某些内容的时长可能会比之前更久。

v7_normalizeFormMethod

提示:

如果你没有使用 <RouterProvider>,则可以跳过此项。

这会将 formMethod 字段规范化为大写的 HTTP 方法,以与 fetch() 函数的行为保持一致。如需了解更多信息,请查看变更日志

👉 启用该特性标志

ts
createBrowserRouter(routes, {
  future: {
    v7_normalizeFormMethod: true,
  },
});

更新你的代码

如果你的任何代码正在检查小写的 HTTP 方法,那么你将需要对其进行更新,改为检查大写的 HTTP 方法(或者对其调用 toLowerCase() 方法来进行转换后再检查)。

👉 将 formMethod 与大写形式进行对比。

ts
useNavigation().formMethod === "post"
useFetcher().formMethod === "get"; 
useNavigation().formMethod === "POST"
useFetcher().formMethod === "GET"; 

v7_partialHydration

提示:

如果你没有使用 <RouterProvider>,则可以跳过此项。

这使得服务器端渲染(SSR)框架能够仅提供部分 hydration(水合作用,此处指在 SSR 中关联服务器端渲染内容与客户端交互等相关操作)数据。你不太可能需要为此担心,只需开启相应特性标志就行。如需了解更多信息,请查看变更日志

👉 启用该特性标志

ts
createBrowserRouter(routes, {
  future: {
    v7_partialHydration: true,
  },
});

更新你的代码

在使用部分水合(hydration)的情况下,你需要提供一个 HydrateFallback 组件,以便在初始水合期间进行渲染。此外,如果你之前使用过 fallbackElement,那么需要将其移除,因为它现在已被弃用。在大多数情况下,你会希望将之前的 fallbackElement 重新用作 HydrateFallback 组件。

👉 HydrateFallback 替换 fallbackElement

tsx
const router = createBrowserRouter(
  [
    {
      path: "/",
      Component: Layout,
       HydrateFallback: Fallback, 
      // or
       hydrateFallbackElement: <Fallback />, 
      children: [],
    },
  ],
);


<RouterProvider
  router={router}
   fallbackElement={<Fallback />} 
/>

v7_skipActionErrorRevalidation

提示:

如果你没有使用 createBrowserRouter,则可以跳过此项。

当启用此特性标志后,在 action 抛出或返回状态码为 4xx/5xxResponse,加载器(loaders)默认将不再重新验证。在这些情况下,你可以通过 shouldRevalidate 以及 actionStatus 参数来选择重新进行验证。

👉 启用该特性标志

ts
createBrowserRouter(routes, {
  future: {
    v7_skipActionErrorRevalidation: true,
  },
});

更新你的代码

在大多数情况下,你可能无需对应用程序代码进行更改。通常来说,如果某个 action 出现错误,数据不太可能已被修改,也就不需要重新验证。如果你的任何代码在 action 出错的场景下确实修改了数据,那你有两个选择:

👉 选项1:修改 action,以避免在出错场景下进行数据变更

tsx
// 修改前
async function action() {
  await mutateSomeData();
  if (detectError()) {
    throw new Response(error, { status: 400 });
  }
  await mutateOtherData();
  // ...
}

// 修改后
async function action() {
  if (detectError()) {
    throw new Response(error, { status: 400 });
  }
  // 现在所有数据在验证后才开始变更
  await mutateSomeData();
  await mutateOtherData();
  // ...
}

👉 选项2:通过 shouldRevalidateactionStatus 选择启用重新验证

tsx
async function action() {
  await mutateSomeData();
  if (detectError()) {
    throw new Response(error, { status: 400 });
  }
  await mutateOtherData();
}

async function loader() { ... }

function shouldRevalidate({ actionStatus, defaultShouldRevalidate }) {
  if (actionStatus != null && actionStatus >= 400) {
    // 当 actions 返回 4xx/5xx 状态时重新验证此加载器
    return true;
  }
  return defaultShouldRevalidate;
}

弃用说明

jsondefer 方法已被弃用,建议改为返回原始对象。

tsx
async function loader() {
  return json({ data }); 
  return { data };  

如果你之前使用 json 方法将数据序列化为JSON格式,那么现在可以改用原生的 Response.json() 方法来替代。

更新到 v7

既然你的应用程序已经完成了上述相关更新(与前文提到的更新内容保持同步),那么理论上你就可以毫无问题地直接更新到版本 7 了。

👉 安装 v7 版本

sh
npm install react-router-dom@latest

👉 用 react-router 替换掉 react-router-dom

在版本 7 中,我们不再需要 “react-router-dom” 了,因为相关的软件包已经进行了简化。你可以从 “react-router” 中导入所有内容。

sh
npm uninstall react-router-dom
npm install react-router@latest

注意在 package.json 中,你只需要 “react-router”

👉 更新导入

改为使用 react-router 导入:

ts
import { useLocation } from "react-router-dom"; 
import { useLocation } from "react-router"; 

你无需手动更新导入语句,可使用这条命令来进行操作。不过要确保你的 Git 工作树是干净的(即没有未提交的更改等情况),这样一来,如果命令运行结果不符合预期,你还可以进行回滚操作。

sh
find ./path/to/src \( -name "*.tsx" -o -name "*.ts" -o -name "*.js" -o -name "*.jsx" \) -type f -exec sed -i '' 's|from "react-router-dom"|from "react-router"|g' {} +

如果你安装了 GNU sed(大多数 Linux 发行版都已安装),那就改用这条命令:

sh
find ./path/to/src \( -name "*.tsx" -o -name "*.ts" -o -name "*.js" -o -name "*.jsx" \) -type f -exec sed -i 's|from "react-router-dom"|from "react-router"|g' {} +

👉 更新特定于 DOM 的导入内容

RouterProviderHydratedRouter 来自深层导入,因为它们依赖于 “react-dom”

ts
import { RouterProvider } from "react-router-dom"; 
import { RouterProvider } from "react-router/dom"; 

请注意,对于非 DOM 上下文(比如 Jest 测试环境),你应该使用顶级导入:

ts
import { RouterProvider } from "react-router-dom"; 
import { RouterProvider } from "react-router"; 

Released under the MIT License.