多くの場合はちゃんとクリーンアップ処理を書けば大丈夫なのですが そうもいかないケースがあります
例えば マウント時に state にデータを追加するような処理がある場合 2 回呼び出されるので 最初から 2 つの要素が存在することになります
const add = () => {
setState(state => [...state, { at: new Date(), value: Math.random() }])
}
useEffect(() => {
add()
const timer = setInterval(add, 1000 * 60)
return () => clearInterval(timer)
}, [])
あまり問題にはならないですが エラーがないと 1 つだけみたいなケースで 開発中は常に 2 つあることになって 実際のものと見た目が異なるので気持ち悪かったりします
また キーになる情報がタイムスタンプくらいだと useEffect が 2 回呼び出された場合はキーが同じになってしまうことがあります
開発時のみの都合で別のキーを自動生成したり index をキーにするのはあまり良い方法とも思えません
とりあえず最初の実行かどうかを ref を使って判断するのですが 追加で ref が増えますし 開発時の都合で特別なことをするのはあまり気持ちの良いものではないです
const ref = useRef(true)
useEffect(() => {
const is_first = ref.current
ref.current = false
if (is_first) {
add()
}
const timer = setInterval(add, 1000 * 60)
return () => clearInterval(timer)
}, [])
クリーンアップ関数を使ってできないかと思ってやってみたのはこれです
useEffect(() => {
const timer1 = setTimeout(add, 1)
const timer2 = setInterval(add, 1000 * 60)
return () => {
clearTimeout(timer1)
clearInterval(timer2)
}
}, [])
一瞬だけ遅延させます
そうすることで 2 回目の useEffect 呼び出しで 1 回目の処理をキャンセルできます
ref が不要で useEffect の中だけで完結するのが良いです
ただし僅かな遅延があるので 見た目上 ちらつき等になる可能性もあります
また 正常な動作として高速で 2 回呼び出された場合にキャンセルが発生します
それも気にするなら ref に頼ることになりそうです
最近は React で新しいものを作るたびこういう不満点を感じて別のフレームワークを探しては 不足点があってまだ使えないなぁを繰り返してます