📝

React Memory leak エラーの出力対策

2021/03/04に公開
2

React Memory leak メモリリーク対策

useEffect でState を扱うときの注意点

React でStateを使うとみかけるこのワーニング。
unmountsしてるけど、メモリ開放ができてないことをあらわしてます。

index.js: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 the componentWillUnmount method.

解決案

unmount しているときはstateにアクセスさせないようにする、というのがよいみたいです。
useEffect の return 処理は unmount するときコールされるのでフラグをたてて抑制します。

useEffect(() => {
  let isMounted = true; // note this flag denote mount status
  someAsyncOperation().then(data => {
    if (isMounted) setState(data);
  })
  return () => { isMounted = false }; // use effect cleanup to set flag false, if unmounted
});

もっと簡単な解決方法

useState に上記の解決案をいれたhooksがあります。

https://www.npmjs.com/package/use-state-if-mounted

import { useStateIfMounted } from "use-state-if-mounted";

const Button = (porps Props) => {
  //  const [visible, setVisible] = useState(false);
  const [visible, setVisible] = useStateIfMounted(false);

useState を useStateIfMounted にするだけで対応できます。
TypeScript 未対応なのが残念です。

余裕ができたら、PR してみようかと思います。

追記)React 18 からはこのエラーが廃止されるそうです。

コメントいただきました。

もともとは下記のようなマウント後に参照を持ち続けるケースを警告するためのエラーだったようです

useEffect(() => {
  function handleChange() {
     setState(store.getState())
  }
  store.subscribe(handleChange)
  // この unsubscribe がない場合はリークします。
  return () => store.unsubscribe(handleChange)
}, [])

ので、私が書いたケースはエラーはでるが実質的にはリークしてないようです。

sosoさんが検証されています。素晴らしいです。

https://zenn.dev/so_nishimura/articles/c4df97cd1937c2

ということで、 React 18にあげましょう!

Discussion

coa00coa00

有益な情報ありがとうございます。
ちょうど続編を書こうと思ったのですが、soso さんの記事、引用もしっかりしていてこちら読んだほうがいいですね。