👬
React なんで2回走るの?
ムーザルちゃんねるのムーです。今回は @zaru さんと「React の StrictMode で処理が2回走ること」について話しました。
React に入門してコードを書いていると「あれ、なんかここ 2 回実行されてない?」となる瞬間、あると思います。それについての話になります。
- なんで2回走るのかわからない
- StrictMode について知らない、知ってるけど2回走る意味がわからない
- どこの処理が2回走るかわからない
という方に向けた動画となっています。
以下に簡単なサマリを書いておきます。
話したこと
- StrictMode があります
- なぜ StrictMode は 2 回走らせているのか?
- どこで 2 回走るの? (え、そこも2回走るの?)
StrictMode があります。
これによって、2 回走ります。
off にすれば、2 回走らなくなります。
ではなぜ 2 回走らせているのか?
React コンポーネントは、純粋関数であることが前提とされています。
純粋関数であれば、何度走っても結果は同じで、困らないはず。
複数回走って結果が変わる → 純粋じゃない → よくない(バグの可能性) → バグ早期発見!
結論: 純粋関数でない不具合の可能性のあるコードを発見しやすくするために、2 回走っています。
例: 純粋じゃない React component
let x = 0;
function NotPure() {
x = x + 1;
return <div>{x}</div>;
}
// コンポーネントの呼び出しタイミングで表示される内容が変わってしまう
<NotPure /> // 1
<NotPure /> // 2
2回走るものは何があるの?
- component function
- useState, setSomething, useMemo, useReduce に渡される関数
- など
詳細はこちら
なんで、setSomething のコールバックも2回走るの?
const [something, setSomething] = useState(null)
const handler = () => {
setSomething(p => {
return p + 1;
}
}
このときの、setSomething の引数の関数も2回呼ばれます。
ですので、たとえば、以下のようなコードはよくないです。
// 経験値
const [exp, setExp] = React.useState(0);
// レベル
const [level, setLevel] = React.useState(1);
// よくない例
// 経験値が 100 を超えたら、レベルをひとつあげる
const ngGainExp = () => {
setExp((prev) => {
const newExp = prev + 20;
if (newExp >= 100) {
setLevel((prev) => prev + 1); // 副作用があり、pure じゃなくなっている
return 0;
}
return newExp;
});
};
上を修正するアイデアとして以下のような方法があります。あるいは、state をひとつにしてしまうのも良いでしょう。
(動画でも話しましたが、以下の例はいまいちかもしれない...)
// 修正した例
const gainExp = () => {
const newExp = exp + 20;
setExp(newExp >= 100 ? 0 : newExp);
// setExp のコールバックから setLevel を引き離した
if (newExp >= 100) {
setLevel((prev) => prev + 1);
}
};
ひとこと、まとめ
- pure 関数で書こう!
Discussion