ReactではuseEffect を使ってしまうことがあります。
以前、ワード数が一定件数を超えたときに警告メッセージを表示する処理を useEffect で書いていました。
ただ、レビューの際に、そこで本当に反応したかったのは「値の変化」ではなく「データ取得完了」というイベントでした。
今回は、その経験から useEffect の使い方を見直した話を書きます。
最初に書いていたコード
最初は、ワード数が550件以上のときに toast を表示するために、次のように useEffect を使っていました。
useEffect(() => {
if (displayedWordCount >= 550) {
const warningToastId = toast.warning(`表示件数が${displayedWordCount}件です`)
return () => {
toast.dismiss(warningToastId)
}
}
}, [analysisResultId, displayedWordCount])
このコードでも動作はします。
ただ、この書き方だと displayedWordCount が変化するたびに effect が再実行されます。
そのため、条件次第では toast が意図せず再表示される可能性があります。
また、依存配列に analysisResultId と displayedWordCount を並べていると、「何に反応したい処理なのか」が少し分かりづらくなります。
本当に反応したかったのは何か
この処理で本当にやりたかったのは、ワード数の変化を監視することではありませんでした。
やりたかったのは、次のことです。
データ取得が完了した
取得した件数を確認する
550件以上なら警告を出す
つまり、反応したかったトリガーは「件数の変化」ではなく、「データ取得成功」というイベントでした。
そこで、useEffect で監視するのではなく、データ取得成功時の onSuccess で処理するように変更しました。
const { data: analysisRows, isLoading, error } = useAnalysisRows(analysisResultId, {
onSuccess: (response) => {
const fetchedRowCount = response.data.length
if (fetchedRowCount >= 550) {
toast.warning(`表示件数が${fetchedRowCount}件です`)
}
},
})
この形にすると、処理が明確になりました。
データ取得成功時に
取得件数を見て
条件を満たしたら通知する
という流れが、そのままコードに表れています。
変更してよかった点
1 . 何をきっかけに処理が走るのか分かりやすい
useEffect だと「依存値が変わったから走る」になります。
一方、onSuccess だと「データ取得成功時に走る」とはっきり書けます。
今回のケースでは、後者の方が意図に合っていました。
2 . 不要な再実行を避けやすい
useEffect は依存値の変化で何度でも実行されます。
そのため、今回のような通知処理では再表示の制御を意識する必要があります。
まとめ
- useEffect は React外との同期に使う
- 実行タイミングが明確なら onSuccess やイベントハンドラの方が自然