RX100で撮り歩くブログ

RX100やRollei B35で写真を撮ったり何やかんやするブログ

ReactのuseEffectでcleanupするように言われた時~To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function

Reactで開発していてコンポーネントのアンマウント時に以下のようなエラーが出る場合があります。

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.

これは副作用(effect)フックを使うときに出ることがあります。

ja.reactjs.org

ドキュメントに書かれてるように useEffectcomponentDidMountcomponentDidUpdatecomponentWillUnmount がまとまったようなものです。

このuseEffect内で非同期処理を行ない、非同期処理が完了する前にページ遷移などでコンポーネントをアンマウントした場合に、非同期処理完了後に更新するコンポーネントがなくなっているため、冒頭のようなエラーが発生します。

例えば以下のように書くと、1500ミリ秒経過する前にページ遷移すると、1500ミリ秒後にsetDataが更新するコンポーネントがなくなってしまいエラーとなります。動きはするので一見動作してるように見えますが。

const [data, setData] = React.useState<boolean>(true)

React.useEffect(() => {
    setTimeout(() => {
        setData(false)
    }, 1500)
})

先ほどuseEffectは、componentWillUnmount の役割もあると書きましたが、 useEffect() から関数を返せば componentWillUnmount となります。

なので、以下のようにcleanup処理を書くとエラーが発生しなくなります。unmountedを定義しておき、アンマウントされた時にcleanup関数でunmountedをtrueにしておくことで非同期処理完了後の処理を動作しないようにします。

React.useEffect(() => {
  let unmounted = false

 // async await でも同様、const data = await getData();
  setTimeout(() => {
    if (!unmounted) {
      setIsLoadingTime(false)
    }
  }, 1500)

  // clean up関数(Unmount時の処理)
  return () => {
    unmounted = true
  }
})