そんななので今更気づいたのですが 18 では コンポーネントのアンマウント後に state を更新したときに出ていた警告がなくなっていました
こういうの
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.
表示させる例
export default () => {
const [shown, setShown] = useState(false)
return (
<div>
<button onClick={() => setShown(!shown)}>toggle</button>
{shown && <Component/>}
</div>
)
}
const Component = () => {
const [count, setCount] = useState(0)
const onClick = () => {
setTimeout(() => {
setCount(count => count + 1)
}, 3000)
}
return (
<div>
<button onClick={onClick}>{count}</button>
</div>
)
}
toggle ボタンのクリックで Component の有無を切り替えます
Component はボタンをクリックするとカウントアップするコンポーネントですが 3 秒ディレイしてからカウントアップします
この 3 秒の間に Component を表示しないようにすると アンマウントされて その後に state の更新が起きるので警告が出ます
実際のケースでは fetch など非同期処理を行うコンポーネントで その処理が完了する前に別のページに遷移したりタブを切り替えたりすることで発生することが多いです
これの対処をちゃんとやると面倒です
アンマウント済みかを示すフラグを用意してアンマウント時に更新します
state 更新時にはフラグを見てアンマウント済みなら更新をスキップします
面倒な上にコードも複雑化する嫌なやつです
実際こういう一回限りの setState がアンマウント後に行われてもメモリリークにはならないです
それでも警告として出る以上対処しようとして その結果 面倒が多く複雑になるという問題があったのでなくしたのかもですね
メモリリークになるのは setInterval や外部のサブスクリプションなどで 1 回限りではなくイベントが起きるたびに更新が発生するものです
こういうのです
useEffect(() => {
setInterval(() => {
setState(Date.now())
}, 1000)
}, [])
この警告は出ていても無視していいケースが多かったですが 無くしてしまうとこういうケースに気づけないという問題はありそうです