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を利用すると適切なルーティングの設計を促されているように感じるため、こちらを推奨したい。