なぜ、ErrorBoundary から復帰できないのか
ErrorBoundary とは
React では、ErrorBoundaryというコンポーネントが存在します。
Suspense のように、エラーが発生した際にはエラーを Throw し、ErrorBoundary でキャッチする事ができ、fallback を表示させる事ができます。
ErrorBoundary でエラーがキャッチされた際に fallback から復帰できない
例えば、下記のようなコードがあるとします。
const App = () => { const navigate = useNavigate() const params = new URLSearchParams(document.location.search); const name = params.get("name"); const handleSubmit = (e) => { e.preventDefault(); const formData = getFormData(e.currentTarget); navigate(`/?name=${formData.name}`); } return ( <> <ErrorBoundary fallback={<p>Something went wrong</p>}> <Suspense fallback={<div>loading<div>}> <DisplayData name={name} /> </Suspense> </ErrorBoundary> <form onSubmit={handleSubmit}> <input name="name"> <button type="submit">submit</button> </form> </> ); };
input に入力した値を URL パラメータに載せ、navigate(ページ遷移)してします。そしてその URL パラメータで Suspend を発生させています。
もし、DisplayData 内でエラーが発生した場合、ErrorBoundary でキャッチされ fallback が表示されるので、form には影響がいきません。
なので、もう一度 Submit し、再レンダリングを起こす事ができます。
ただ、一度 ErrorBoundary でエラーがキャッチされた後、再度 Submit を行なっても、ErrorBoundary の fallback が表示されたままです。
なぜなのでしょうか?
なぜ、fallback から復帰できないのか
なぜ、再レンダリングを行なっても、ErrorBoundary の fallback が表示されたままなのでしょうか?
これは ErrorBoundary の fallback が表示されたまま再レンダリングを行なっても、コンポーネントツリーからは、ErrorBoundary の children が消えているからです。
なので、この状態で再レンダリングを行なっても、ErrorBoundary の fallback を再レンダリングしているにすぎないです。
対処法
対処法は簡単です。
const App = () => { const navigate = useNavigate() const params = new URLSearchParams(document.location.search); const name = params.get("name"); const handleSubmit = (e) => { e.preventDefault(); const formData = getFormData(e.currentTarget); navigate(`/?name=${formData.name}`); } return ( <> <ErrorBoundary key={name} fallback={<p>Something went wrong</p>}> <Suspense fallback={<div>loading<div>}> <DisplayData name={name} /> </Suspense> </ErrorBoundary> <form onSubmit={handleSubmit}> <input name="name"> <button type="submit">submit</button> </form> </> ); };
ErrorBoundary に key を挿して、再度レンダリングをさせてあげれば、ErrorBoundary の children ごとレンダリングされます。
key が変わる事で起こるレンダリングは再レンダリングではなく、初期レンダリングなんですね。
なので、その際 children が復活してくれるという事です。