v6 からのアップグレード

React Router v7 では、以下の最小バージョンが必要です。

  • node@20
  • react@18
  • react-dom@18

v7 へのアップグレードは、すべての future フラグを有効にしている場合は、破壊的な変更はありません。これらのフラグを使用すると、アプリを一度に 1 つずつ変更できます。一度にすべてを行うのではなく、各ステップの後にコミットして出荷することを強くお勧めします。

最新の v6.x に更新する

まず、最新の future フラグとコンソール警告を取得するために、v6.x の最新のマイナーバージョンに更新します。

👉 最新の v6 に更新する

npm install react-router-dom@6

v7_relativeSplatPath

背景

dashboard/* (単なる * ではなく) のような複数セグメントのスプラットパスの相対パスのマッチングとリンクを変更します。詳細については、CHANGELOG を参照してください。

👉 フラグを有効にする

フラグの有効化は、ルーターのタイプによって異なります。

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

コードを更新する

<Route path="dashboard/*"> のようにパスとスプラットを持つルートがあり、その下に <Link to="relative"><Link to="../relative"> のような相対リンクがある場合は、コードを更新する必要があります。

👉 <Route> を 2 つに分割する

複数セグメントのスプラット <Route> を、パスを持つ親ルートとスプラットを持つ子ルートに分割します。

<Routes>
  <Route path="/" element={<Home />} />
-  <Route path="dashboard/*" element={<Dashboard />} />
+  <Route path="dashboard">
+    <Route path="*" element={<Dashboard />} />
+  </Route>
</Routes>
 
// または
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>
  );
}

v7_startTransition

背景

これは、ルーターの状態更新に React.useState の代わりに React.useTransition を使用します。詳細については、CHANGELOG を参照してください。

👉 フラグを有効にする

<BrowserRouter
  future={{
    v7_startTransition: true,
  }}
/>
 
// または
<RouterProvider
  future={{
    v7_startTransition: true,
  }}
/>

👉 コードを更新する

コンポーネントの 内部React.lazy を使用している場合を除き、何も更新する必要はありません。

コンポーネントの内部で React.lazy を使用することは、React.useTransition (またはコンポーネントの内部で Promise を作成するその他のコード) と互換性がありません。React.lazy をモジュールスコープに移動し、コンポーネントの内部で Promise を作成するのをやめてください。これは React Router の制限ではなく、React の誤った使用法です。

v7_fetcherPersist

<RouterProvider> を使用していない場合は、これをスキップできます

背景

フェッチャーのライフサイクルは、所有者コンポーネントがアンマウントされたときではなく、アイドル状態に戻ったときに基づくようになりました。詳細については、CHANGELOG を参照してください。

フラグを有効にする

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

コードを更新する

アプリに影響を与える可能性は低いでしょう。以前よりも長く持続する可能性があるため、useFetchers の使用状況を確認することをお勧めします。何をしているかによっては、以前よりも長くレンダリングされる場合があります。

v7_normalizeFormMethod

<RouterProvider> を使用していない場合は、これをスキップできます

これは、fetch() の動作に合わせて、formMethod フィールドを大文字の HTTP メソッドとして正規化します。詳細については、CHANGELOG を参照してください。

👉 フラグを有効にする

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

コードを更新する

コードのいずれかが小文字の HTTP メソッドをチェックしている場合は、大文字の HTTP メソッドをチェックするように更新する必要があります (または、toLowerCase() を呼び出す必要があります)。

👉 formMethod を大文字と比較する

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

v7_partialHydration

<RouterProvider> を使用していない場合は、これをスキップできます

これは、主に SSR フレームワークで使用されるデータルーターの部分的なハイドレーションを有効にしますが、ルートモジュールをロードするために lazy を使用している場合にも役立ちます。これを心配する必要はほとんどありません。フラグをオンにするだけです。詳細については、CHANGELOG を参照してください。

👉 フラグを有効にする

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

コードを更新する

部分的なハイドレーションでは、初期ハイドレーション中にレンダリングする HydrateFallback コンポーネントを提供する必要があります。さらに、以前に fallbackElement を使用していた場合は、非推奨になったため削除する必要があります。ほとんどの場合、fallbackElementHydrateFallback として再利用することをお勧めします。

👉 fallbackElementHydrateFallback に置き換える

const router = createBrowserRouter(
  [
    {
      path: "/",
      Component: Layout,
+      HydrateFallback: Fallback,
      // または
+      hydrateFallbackElement: <Fallback />,
      children: [],
    },
  ],
);
 
 
<RouterProvider
  router={router}
-  fallbackElement={<Fallback />}
/>

v7_skipActionErrorRevalidation

createBrowserRouter を使用していない場合は、これをスキップできます

このフラグが有効になっている場合、アクションが 4xx/5xx ステータスコードで Response をスロー/返す場合、ローダーはデフォルトで再検証されなくなります。これらのシナリオでは、shouldRevalidateactionStatus パラメーターを使用して再検証を選択できます。

👉 フラグを有効にする

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

コードを更新する

ほとんどの場合、アプリのコードを変更する必要はないでしょう。通常、アクションがエラーになった場合、データが変更されて再検証が必要になる可能性は低いでしょう。コードのいずれかがアクションエラーシナリオでデータを 変更する 場合は、2 つのオプションがあります。

👉 オプション 1: エラーシナリオでの変更を避けるように action を変更する

// 以前
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 を使用して再検証を選択する

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) {
    // アクションが 4xx/5xx ステータスを返すときにこのローダーを再検証する
    return true;
  }
  return defaultShouldRevalidate;
}

非推奨

json および defer メソッドは、生のオブジェクトを返すことが推奨されるため、非推奨になりました。

async function loader() {
- return json({ data });
+ return { data };

json を使用してデータを JSON にシリアル化していた場合は、代わりにネイティブの Response.json() メソッドを使用できます。

v7 にアップグレードする

アプリが追いついたので、問題なく 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 固有のインポートを更新する

RouterProviderHydratedRouter は、"react-dom" に依存するため、深いインポートから取得されます。

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

Jest テストなど、DOM 以外のコンテキストでは、トップレベルのインポートを使用する必要があることに注意してください。

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

おめでとうございます。これで v7 になりました。