業務でReact Router を使っていて最近移行したので実際に移行の際に実施したことをまとめます!
前提
React Router のバージョン:6.23.1
以下の順で移行を進めました
- v6の最新までアップデート
- v7にアップデート
こちらのChangelogを調べて、破壊的変更(Breaking Change)がないか確認しました。
特になかったのでv6.30.0までアップデートしました。
上の参考記事ではv7のfutureフラグを使用して段階的に移行していましたが、私が所属するチームのコードではfutureフラグで影響がある箇所がなかった為一気にv7まで上げました。
🔍 v7のfutureフラグで影響があるか調べた内容
v7_relativesplatpath
dashboard/* (単なる * ではなく) のような複数セグメントのスプラットパス(* を含むパス)の相対パスのマッチングとリンクを変更します。
を 2 つに分割する
コードにあったcreateBrowserRouterは単なる*でしたので対象外でした。
const router = Router.createBrowserRouter([
{
path: "*",
Component: () => (
AxiosProvider>
Sentry.ErrorBoundary fallback={({ error }) => Error error={error} />}>
AppRoot />
Router.ScrollRestoration />
/Sentry.ErrorBoundary>
/AxiosProvider>
),
},
]);
相対リンクを更新する
相対パスではないので対象外でした。
Link
to="https://www.immedio.io/sample"
target="_blank"
~~略~~~~~
Link
to="https://www.immedio.io/sample2"
v7_startTransition
React.lazy がコンポーネント外部で定義されているので対応不要でした。
v7_fetcherPersist
useFetchersを使用している記述がないので対応不要でした。
v7_normalizeFormMethod
formMethodの記述がないので対応不要でした。
v7_partialHydration
fallbackElement を実装していないので対応は不要でした。
v7_skipActionErrorRevalidation
async function actionの記述がないので対応は不要でした。
非推奨
json および defer メソッド
jsonメソッドはネイティブのものしかないので対応不要でした
deferメソッドは実装していないので対応不要
🚧 react-routerをv7にアップデートを実施
ライブラリをinstall
-
react-router-dom@latestをinstallする -
react-router-domをuninstallします
react-routerのインポートを更新する
コマンドで一括置換できます
以下はdocumentにあるサンプルのコマンドです
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";
おまけ tsconfigの設定を修正
修正した経緯
react-router-router/domの型定義ファイルの形式がmtsでした。
tsconfigのmoduleResolutionがnodeの為、深いimportができませんでした。
moduleResolutionをnodeから bundlerへ、allowImportingTsExtensions:trueを追加しました。
{
"compilerOptions": {
"target": "es6",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"allowImportingTsExtensions":true,
"noEmit": true,
"jsx": "react-jsx",
"types": ["vite/client"],
"jsxImportSource": "@emotion/react",
"baseUrl": "./src",
"paths": {
"~/*": ["./*"]
}
},
"sourceMap": true,
"inlineSources": true,
"sourceRoot": "https://zenn.dev/",
"include": ["src"]
}
修正するまでの過程

Cannot find module ‘react-router/dom’ or its corresponding type declarations.
There are types at ‘/Users/yamanetaisei/dev/immedio/node_modules/react-router/dist/development/dom-export.d.mts’, but this result could not be resolved under your current ‘moduleResolution’ setting. Consider updating to ‘node16’, ‘nodenext’, or ‘bundler’.
tsconfigのmoduleResolutionをnodenext、moduleをNodeNextにすると、
Relative import paths need explicit file extensions in ECMAScript imports when ‘–moduleResolution’ is ‘node16’ or ‘nodenext’. Did you mean ‘./util/errors.js’?

An import path can only end with a ‘.ts’ extension when ‘allowImportingTsExtensions’ is enabled.
allowImportingTsExtensionsをtrueにすると、moduleResolutionをbundler,noEmitかemitDeclarationOnlyを設定する必要がある
Allow imports to include TypeScript file extensions. Requires ‘–moduleResolution bundler’ and either ‘–noEmit’ or ‘–emitDeclarationOnly’ to be set.
useNavigateが非同期になったことによる影響調査
useNavigateが使われている周辺コードを確認しましたが、画面遷移とあわせてスナックバーを表示するのみなので影響はないと判断しました。
useNavigateは以下の場合にawaitにする必要があります。
- 画面遷移後に続けて実行したい操作がある場合(inputにフォーカスあてるなど)
- 画面遷移先のページにloader, actionがあり、そのエラーハンドリングを行いたい場合
ページ遷移に関わる処理でしたが、無事に問題なく移行できました!
Views: 2
