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のリポジトリ見にいったけどさっぱりわかりませんでした。どこ読めばわかるかわかった人いたら教えてください💕

