if文の中でuseMemoを使ったときの面白い挙動
はじめに
if文の中でhooksを使ったらダメなのはみなさんご存知ですよね。
もしif文の分岐でhooksが呼ばれる回数が変わったら以下のようにエラーがでて怒られます。
もしif文の分岐があってもhooksが呼び出される回数や順番に変更がなければどうなるのでしょうか?
そのときの面白い挙動を紹介します。
環境
"react": "^17.0.2",
やってみた。
以下のようなプログラムを書きます。
/* eslint-disable react-hooks/rules-of-hooks */
import React, { useMemo, useState } from "react";
import "./App.css";
function App() {
const [flag, setFlag] = useState(true);
let memo1 = 0;
let memo2 = 0;
let memo3 = 0;
if (flag) {
memo1 = useMemo(() => 1, []);
}
memo2 = useMemo(() => 2, []);
if (!flag) {
memo3 = useMemo(() => 3, []);
}
const value = { memo1, memo2, memo3 };
return (
<div className="App">
<button onClick={() => setFlag((_flag) => !_flag)}>Toggle</button>
<br />
{JSON.stringify(value)}
</div>
);
}
export default App;
memo1
,memo2
,memo3
はそれぞれ0で初期化されており、
flag
がtrue
の場合、memo1
がuseMemo
からの値を参照し1になり、flag
がfalse
の場合、memo3
からの値を参照し3になる想定です。memo2
は常にuseMemo
からの値を参照し2になります。
flag
の初期値はtrue
でToogleボタンをクリックするとtrue
とfalse
が入れ替わります。
初回は上記のような表示になります。
memo1
,memo2
,memo3
は以下の値です。
{"memo1":1,"memo2":2,"memo3":0}
別に問題なさそうですね。
ではToogleボタンをクリックしてflag
をfalse
にします。
おや...!?
予想だと{"memo1":0,"memo2":2,"memo3":3}
になると思ったんですが。
memo2に値を代入しているuseMemo
常に2を返しているはずなのに値が1になっています。またmemo3に値を代入しているuseMemo
も常に3を返しているはずなのに値は2になっています。
これはおもしろいですね。
考察
関数の外側からはuseMemoがどこで呼ばれたかではなく、何回よばれたかという情報しかわからないからこういう挙動なんだろうと思う。もしどこで呼び出したかまでわかるようにするとしたらkey
みたいな概念が必要と思われる。きになってReactのリポジトリ見にいったけどさっぱりわかりませんでした。どこ読めばわかるかわかった人いたら教えてください💕