React Router v7 需要以下最低版本
node@20
react@18
react-dom@18
如果您已啟用所有未來標誌,則 v7 升級沒有重大變更。這些標誌讓您可以一次更新應用程式一個變更。我們強烈建議您在每個步驟後進行提交並發布,而不是一次完成所有操作。
首先更新到最新的 v6.x 次要版本,以擁有最新的未來標誌和控制台警告。
👉 更新到最新的 v6 版本
npm install react-router-dom@6
背景
變更多段 splat 路徑(例如 dashboard/*,相對於僅 *)的相對路徑匹配和連結。查看更新日誌以取得更多資訊。
👉 啟用標誌
啟用標誌取決於路由器的類型
<BrowserRouter
future={{
v7_relativeSplatPath: true,
}}
/>
createBrowserRouter(routes, {
future: {
v7_relativeSplatPath: true,
},
});
更新您的程式碼
如果您有任何路徑加上 splat 的路由,例如 <Route path="dashboard/*">
,且在其下方有相對連結,例如 <Link to="relative">
或 <Link to="../relative">
,則您需要更新程式碼。
👉 將 <Route>
分割成兩個
將任何多段 splat <Route>
分割成具有路徑的父路由和具有 splat 的子路由
<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>
元素,以包含額外的 ..
相對段,以繼續連結到相同的位置
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>
);
}
背景
這使用 React.useTransition
而不是 React.useState
來更新 Router 狀態。查看更新日誌以取得更多資訊。
👉 啟用標誌
<BrowserRouter
future={{
v7_startTransition: true,
}}
/>
// or
<RouterProvider
future={{
v7_startTransition: true,
}}
/>
👉 更新您的程式碼
除非您在元件內部使用 React.lazy
,否則您不需要更新任何內容。
在元件內部使用 React.lazy
與 React.useTransition
(或其他在元件內部建立 Promise 的程式碼)不相容。將 React.lazy
移動到模組作用域,並停止在元件內部建立 Promise。這不是 React Router 的限制,而是 React 的不正確用法。
<RouterProvider>
,則可以跳過此步驟
背景
Fetcher 的生命週期現在基於其返回閒置狀態的時間,而不是其擁有者元件卸載的時間:查看更新日誌以取得更多資訊。
啟用標誌
createBrowserRouter(routes, {
future: {
v7_fetcherPersist: true,
},
});
更新您的程式碼
這不太可能影響您的應用程式。您可能需要檢查 useFetchers
的任何用法,因為它們的持久性可能比以前更長。根據您正在執行的操作,您可能會渲染比以前更長的時間。
<RouterProvider>
,則可以跳過此步驟
這將 formMethod
欄位標準化為大寫 HTTP 方法,以與 fetch()
行為對齊。查看更新日誌以取得更多資訊。
👉 啟用標誌
createBrowserRouter(routes, {
future: {
v7_normalizeFormMethod: true,
},
});
更新您的程式碼
如果您的任何程式碼正在檢查小寫 HTTP 方法,您將需要更新它以檢查大寫 HTTP 方法(或對其呼叫 toLowerCase()
)。
👉 將 formMethod
與大寫進行比較
-useNavigation().formMethod === "post"
-useFetcher().formMethod === "get";
+useNavigation().formMethod === "POST"
+useFetcher().formMethod === "GET";
<RouterProvider>
,則可以跳過此步驟
這啟用了資料路由器的部分水合,主要用於 SSR 框架,但如果您使用 lazy
來載入路由模組,它也很有用。您不太可能需要擔心這個,只需開啟標誌即可。查看更新日誌以取得更多資訊。
👉 啟用標誌
createBrowserRouter(routes, {
future: {
v7_partialHydration: true,
},
});
更新您的程式碼
使用部分水合時,您需要提供一個 HydrateFallback
元件以在初始水合期間渲染。此外,如果您之前使用過 fallbackElement
,則需要將其移除,因為它現在已棄用。在大多數情況下,您會希望將 fallbackElement
重複用作 HydrateFallback
。
👉 將 fallbackElement
替換為 HydrateFallback
const router = createBrowserRouter(
[
{
path: "/",
Component: Layout,
+ HydrateFallback: Fallback,
// or
+ hydrateFallbackElement: <Fallback />,
children: [],
},
],
);
<RouterProvider
router={router}
- fallbackElement={<Fallback />}
/>
createBrowserRouter
,則可以跳過此步驟
啟用此標誌後,在動作拋出/返回具有 4xx/5xx 狀態碼的 Response 後,載入器將不再預設重新驗證。您可以透過 shouldRevalidate
和 actionStatus
參數選擇在這些情況下進行重新驗證。
👉 啟用標誌
createBrowserRouter(routes, {
future: {
v7_skipActionErrorRevalidation: true,
},
});
更新您的程式碼
在大多數情況下,您可能不必對應用程式程式碼進行變更。通常,如果動作發生錯誤,則資料不太可能被修改且需要重新驗證。如果您的任何程式碼在動作錯誤情況下確實修改了資料,則您有 2 個選項
👉 選項 1:變更動作以避免在錯誤情況下進行修改
// Before
async function action() {
await mutateSomeData();
if (detectError()) {
throw new Response(error, { status: 400 });
}
await mutateOtherData();
// ...
}
// After
async function action() {
if (detectError()) {
throw new Response(error, { status: 400 });
}
// All data is now mutated after validations
await mutateSomeData();
await mutateOtherData();
// ...
}
👉 選項 2:透過 shouldRevalidate
和 actionStatus
選擇重新驗證
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) {
// Revalidate this loader when actions return a 4xx/5xx status
return true;
}
return defaultShouldRevalidate;
}
json
和 defer
方法已被棄用,建議改為返回原始物件。
async function loader() {
- return json({ data });
+ return { data };
如果您過去使用 json
將資料序列化為 JSON,則可以改用原生 Response.json() 方法。
現在您的應用程式已趕上進度,您可以簡單地更新到 v7(理論上!)而不會出現問題。
👉 安裝 v7
npm install react-router-dom@latest
👉 將 react-router-dom 替換為 react-router
在 v7 中,我們不再需要 "react-router-dom",因為套件已簡化。您可以從 "react-router" 匯入所有內容
npm uninstall react-router-dom
npm install react-router@latest
請注意,您的 package.json 中只需要 "react-router"。
👉 更新匯入
現在您應該更新您的匯入以使用 react-router
-import { useLocation } from "react-router-dom";
+import { useLocation } from "react-router";
除了手動更新匯入,您可以使用此命令。但請確保您的 git 工作樹是乾淨的,以便在它沒有如預期運作時可以還原。
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 發行版),請改用此命令
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 專用匯入
RouterProvider
和 HydratedRouter
來自深層匯入,因為它們依賴 "react-dom"
-import { RouterProvider } from "react-router-dom";
+import { RouterProvider } from "react-router/dom";
請注意,對於非 DOM 環境(例如 Jest 測試),您應該使用頂層匯入
-import { RouterProvider } from "react-router-dom";
+import { RouterProvider } from "react-router";
恭喜,您現在已升級到 v7!