useEffectを使っているとこんなWARNINGが出てきました。
React Hook useEffect has a missing dependency: ‘xxx’. Either include it or remove the dependency array
普段なら指示された通りに書き直して、うまくいけば「よし!」ってやってました。
ただ、いつまでもそれはまずいよな、と思い今回調べてみることにしました。
そこで useEffect について学んだことを書きたいと思います。
useEffect の第二引数どうする?
「React Hook useEffect has a missing dependency: ‘xxx’. Either include it or remove the dependency array」が出るってことは useEffect の第二引数の問題です。
第二引数によって useEffect が実行されるかどうかが決まります。
んで、このとき第二引数に何を入れるかで考えられるのは3パターン
① そもそも第二引数なし
useEffect(() => {
なんかの処理
})
② 配列にxxxを加える
useEffect(() => {
なんかの処理
}, [xxx])
③ 空配列
useEffect(() => {
なんかの処理
}, [])
それぞれどう違うのか次のコードを元に調べたいと思います。
ボタンを押すことで state が変化し、EffectPageコンポーネントが再レンダーされます。
その際に、useEffectが実行されるかどうかを見ていきます。
import React, { useEffect, useState } from 'react'
export const EffectPage = () => {
const [count, setCount] = useState(0)
// useEffectはただログを出すだけ
useEffect(() => {
console.log(count)
}) // 第二引数を変えることで挙動の違いを調べる
return (
<div>
<p>{`count: ${count}`}</p>
<button onClick={() => setCount(count + 1)}>click</button>
</div>
)
}
① 第二引数 なし
useEffect(() => {
console.log(count)
}) // 第二引数はなし
ボタンを押すたびにコンソールに count の値が出力されます。
これは
僕「ボタンをポチッとな」
React「お、ボタンがクリックされたな。countを +1 しよ」
React「そうするとstateが変化したからEffectPageコンポーネントを再レンダーしよっと」
React「EffectPageコンポーネントを再レンダーしたから useEffect を再実行しなきゃ」
React「この useEffect は第二引数がないから実行っと」
useEffect「コンソールに count を出力しましたよ」
って感じで、第二引数がないからレンダーされるたびに useEffec が実行されるからですね
② 第二引数 [count]
useEffect(() => {
console.log(count)
}, [count]) // 第二引数の配列にcountを追加
これもボタンを押すたびにコンソールに count が出力されます。さっきと同じですね。
でも処理はちょっと違います。
僕「ボタンをポチッとな」
React「お、ボタンがクリックされたな。countを +1 しよ」
React「そうするとstateが変化したからEffectPageコンポーネントを再レンダーしよっと」
React「EffectPageコンポーネントを再レンダーしたから useEffect を再実行しなきゃ」
※ここまでは「第二引数なし」と同じ
React「この useEffect は第二引数が [count] だ、countが変化したわけだからこの useEffect を実行っと」
useEffect「コンソールに count を出力しましたよ」
第二引数に count があると、再レンダー時に count が変化していると useEffect が実行されます。
「第二引数なし」との違いは count が変化しているかどうかをチェックしていることです。
「第二引数なし」だとレンダーのたびに useEffect が実行されますが、「第二引数にcountがある」とcountが変化した時だけ useEffect が実行されます。こうすると useEffect の実行回数を減らせるので最適化できますね。
③ 第二引数 [ ]
useEffect(() => {
console.log(count)
}, []) // 第二引数に[]
これはボタンを押してもコンソールに反応はありません。
これは
僕「ボタンをポチッとな」
React「お、ボタンがクリックされたな。countを +1 しよ」
React「そうするとstateが変化したからEffectPageコンポーネントを再レンダーしよっと」
React「EffectPageコンポーネントを再レンダーしたから useEffect を再実行しなきゃ」
※ここまでは①②と同じ
React「この useEffect は第二引数が [ ] だ、さっきと変わらないからこの useEffect はスルーっと」
useEffect「・・・」※コンソールには出力されない
第二引数が [ ] だと再レンダー時に [ ] は変化しないので useEffect はスルーされます。
つまり最初のレンダーのみ処理が実行される、というわけです。
あと今回は「React Hook useEffect has a missing dependency: ‘count’. Either include it or remove the dependency array」が出たと思います。
これは
React「この useEffect は第二引数が [ ] だけど、処理に count を使ってるな。」
React「countに依存してるんだから第二引数にcountを加えるか、第二引数なしにした方がいいよ!」
って注意してくれてるんですね。
ここで疑問です。なんで注意してくるんですかね?
React Hook useEffect has a missing dependencyが注意してくれてること
コードをちょっと変更してみました。
import React, { useEffect, useState } from 'react'
export const EffectPage = () => {
const [count, setCount] = useState(0)
useEffect(() => {
// 3秒ごとにcountをコンソールに出力し続ける
const t = setInterval(() => console.log(count), 3000)
return () => clearInterval(t)
}, [])
return (
<div>
<p>{`count: ${count}`}</p>
<button onClick={() => setCount(count + 1)}>click</button>
</div>
)
}
ボタンをクリックするとコンソールに何が出力されると思いますか?
ずっと「0」なんですよね。countは増えているはずなのに。
これはReactがuseEffectが実行されたタイミングでのcountと結びつけて管理しているかららしいです。※自分はこの部分の理解がまだ浅いです!
だからuseEffect内でのcountが更新されずに「0」が出力される、というわけです。
なので
僕「ボタンをポチッとな」
React「お、ボタンがクリックされたな。countを +1 しよ」
React「そうするとstateが変化したからEffectPageコンポーネントを再レンダーしよっと」
React「EffectPageコンポーネントを再レンダーしたから useEffect を再実行しなきゃ」
React「この useEffect は第二引数が [ ] だ、さっきと変わらないからこの useEffect はスルーっと」
useEffect「なんか外の世界だとcountが変わったような気がするけど、気のせいだろ。指示ないし。このままcount(0)を出力し続けよっと」
こんなことが起きてるわけです。
だからReactが
React Hook useEffect has a missing dependency: ‘count’. Either include it or remove the dependency array
と、countが変化したのに、useEffect内のcountがそのままになってるよ!このままだと危ないよ!と注意してくれてるんですね。
このままだと予期せぬ処理をしてしまいエラーを起こしてしまうかもしれません。なので
「React Hook useEffect has a missing dependency: ‘count’. Either include it or remove the dependency array」
と出たときは、指示通りに第二引数にcountを入れるか、第二引数をなしにした方がベターというわけですね。
React Hook useEffect has a missing dependencyを無視する
とはいえ、最初のレンダー時だけuseEffectを実行したいときもあります。
そんな時、WARNINGがわずらわしかったら
useEffect(() => {
console.log(count)
// eslint-disable-next-line
}, [])
// eslint-disable-next-line をWARNINGが出ている行の前に加えましょう。
でもこれやると他の依存関係見落とすかもしれないんで注意がいりますけどね!
まとめ
- 再レンダー時にuseEffect の第二引数が変化している場合、useEffectが実行される
- 逆に第二引数が変化していない場合、useEffectの実行はスルーされる
- 第二引数がなしの場合、useEffectは再レンダー時に必ず実行される
- React Hook useEffect has a missing dependency: ‘xxx’. は、xxxが変化しているにuseEffect内のxxxがそのままになっているよ、危ないよ!という注意
コメント