useEffectって初回に走るんですよ、
これが面倒で面倒で....
かつてのcomponentWillmountやらcomponentDidUpdateやらを使ったclassコンポーネントを
functionコンポーネントに置き換えようとすると悲劇が起こりやすいです。。(今朝の話)
デモンストレーション
import React, { useEffect, useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("初回だけ走るよ〜");
}, []);
useEffect(() => {
console.log("カウントが押されたとき走るよ〜");
}, [count]);
return (
<div>
<span>{count}回押された</span>
<button onClick={() => setCount(count + 1)}>カウントアップ!</button>
</div>
);
}
ボタンが押されるとcountが1upするよくあるやつです。
2回目に出てくるuseEffect
はカウントが押された時だけ走って欲しいとします。
しかし、実際にコンソールをみてみると....
ボタンを一度しか押していないのに、
console.log("カウントが押されたとき走るよ〜");
が二回走ってしまっています。
componentDidUpdate
と全く同じ挙動を期待するなら、一手間必要です。
解決作
フラグを持たせます。
この場合は無駄なレンダリングを避けるためuseRef
を使うのが適切でしょう。
- import React, { useEffect, useState } from "react";
+ import React, { useEffect, useState, useRef } from "react";
export default function App() {
const [count, setCount] = useState(0);
+ const renderFlgRef = useRef(false)
useEffect(() => {
console.log("初回だけ走るよ〜");
}, []);
useEffect(() => {
+ if(renderFlgRef.current) {
console.log("カウントが押されたとき走るよ〜");
+ } else {
+ renderFlgRef.current = true
+ }
}, [count]);
return (
<div>
<span>{count}回押された</span>
<button onClick={() => setCount(count + 1)}>カウントアップ!</button>
</div>
);
}
ちゃんとカウントアップ時のみ走らせることができました。
毎度フラグを作るのだるい~
useEffectがたくさんあるような場合は、その個数分フラグ処理が必要ですね。
正直面倒なのでカスタムフックにします。
useEffectCustom
// 絶対に初回走るな
import { useEffect, useRef } from "react";
const useEffectCustom = (func, dependencyList) => {
const fisrtFlgRef = useRef(true);
useEffect(() => {
if (!fisrtFlgRef.current) {
func();
} else {
fisrtFlgRef.current = false;
}
}, dependencyList);
};
export default useEffectCustom;
index
import React, { useEffect, useState } from "react";
import useEffectCustom from "./useEffectCustom";
export default function App() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("初回だけ走るよ〜");
}, []);
useEffectCustom(() => {
console.log("カウントが押されたとき走るよ〜");
}, [count]);
return (
<div>
<span>{count}回押された</span>
<button onClick={() => setCount(count + 1)}>カウントアップ!</button>
</div>
);
}
これで毎度フラグ処理を実装せずに済みます!
参考資料