Q. React Routerでページ遷移してもErrorBoundaryがリセットされない
React Router を利用している環境で、一度エラーが表示された後に、パスパラメーターが異なる URL に遷移しても、
ErrorBoundary
によって表示されたエラーが画面から消えないと相談を受けた。
状況
書かれていたコードはかなり簡略化して書くと次のようになっていた。
ItemList
のリンクを踏むとパスパラメーターが変わり、ItemDetails
に表示される内容が変わるというページである。つまり、一度ItemDetails
でエラーが出てしまうと、ItemList
で別のアイテムを選択してもエラーが消えないという状況であった。
function Page() { const { id } = useParams(); return ( <div> <ItemList /> <ErrorBoundary fallback={<ErrorFallback />}> <ItemDetails id={id} /> </ErrorBoundary> </div> );}const router = createBrowserRouter([ { path: "/", element: <Layout />, children: [ { index: true, element: <Page />, }, { path: ":id", element: <Page />, }, ], },]);
原因
まず、ErrorBoundary
は React Router のナビゲーションによってリセットされないことは正常に思える。
なぜなら、ErrorBoundary
の fallback が表示された時点で、id
は関係なく、ナビゲーションしても同一のコンポーネントが再利用されるためである。
次のような状態と同じである。
function Page() { const { id } = useParams(); return ( <div> <ItemList /> <ErrorFallback /> </div> );}
対応策その 1 key
key
を利用してErrorBoundary
をリセットすればいい。
function Page() { const { id } = useParams(); return ( <div> <ItemList /> <ErrorBoundary fallback={<ErrorFallback />} key={id}> <ItemDetails id={id} /> </ErrorBoundary> </div> );}
key
の利用に抵抗感がある人もいるかもしれないが、公式ドキュメントでもナビゲーション時にサスペンスバウンダリをリセットする方法としてkey
が紹介されている。ただし気軽に多用していいわけではない。
https://ja.react.dev/reference/react/Suspense#resetting-suspense-boundaries-on-navigation
対応策その 2 errorElement
もう 1 つの方法として、React Router
のerrorElement
を利用する方法がある。
これを利用すると、あまり明確に言及されていない気がするが、ナビゲーション時にErrorBoundary
がリセットされる。
https://reactrouter.com/en/main/route/error-element
ただし、今回のケースでは書き換えが必要だと思われる。
errorElement
を対応箇所にのみ適用させるために、リンク部分をItemList
からLayout
に移動するように変更する必要がある。
function Page() { const { id } = useParams(); return <ItemDetails id={id} />;}const router = createBrowserRouter([ { path: "/", element: <LayoutWithItemList />, children: [ { index: true, element: <Page />, }, { path: ":id", errorElement: <ItemDetailsErrorBounday />, element: <Page />, }, ], },]);
個人的にはerrorElement
を利用すると適切なルーティングの設計を促されているように感じるため、こちらを推奨したい。