はじめに
前ステップから続き、勉強用のuseFetchを書いていきます。
課題
ステップ1のコードを使うと、次のようなワーニングが出る場合があります。
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
破棄されたコンポーネントに対して更新処理をしようとして怒られています。このワーニングを意図的に出すには、useFetchを使っているコンポーネントにkey
プロパティを設定して、それを変更します。1秒以内に破棄されると、その後setResult
を実行するところでワーニングとなります。
ステップ2: cleaned-upフラグ
const useFetch = url => {
const [result, setResult] = useState({});
useEffect(() => {
let cleanedUp = false;
const fetchData = async () => {
const response = await fetch(url);
const data = await response.json();
if (!cleanedUp) {
setResult({ data });
}
};
fetchData();
const cleanup = () => {
cleanedUp = true;
};
return cleanup;
}, [url]);
return result;
};
ステップ1からの差分はcleanupの処理が入ったことです。useEffectで非同期処理を扱う場合はほぼ例外なくcleanup処理を入れることが必須です。今回のケースでは、単純にcleanedUp
フラグを用意してcleanup後にsetResult
を実行しないようにしました。 参考 1
このフラグによる処理の中断を「キャンセル」と読んでいる記事をたまに見かけますが、これは非同期処理のキャンセルとは呼べるものではないので注意してください。
動作確認
実際に動くコードはこちらです。codesandbox
おわりに
本コードは勉強用ですので、そのままでは使わないでください。(ちゃんとした実装はこちら)
さらなる課題と解決は次のステップへ。
-
過去の記事では「アンマウントのフラグ」と書いていましたが、関数コンポーネントにおいてマウント・アンマウントという表現を使うのは正しくない気がするので、「破壊」とか「クリーンアップ」と呼ぶようにしてみようかと思います。ただ、この呼び方もより良いものが見つかれば変えるので、定着したものではありません。 ↩