ReactNのuseGlobalは参照が変化しなくても発火しうる
下に「ボタンを押すとyの値が変わるコード」がある。
コンポーネントComp1はuseGlobal("x")しているのでxが変わった時だけ再描画されることを期待している。
しかし、意外なコードで期待していない再描画が起きたのでメモする。
code:ts
const TestApp = () => {
console.log("render TestApp");
return (
<div>
<Comp1 />
<Comp2 />
</div>
);
};
const Comp1 = () => {
const x = useGlobal("x"); console.log("render Comp1");
return <span>{x}</span>;
};
const Comp2 = () => {
const onClick = () => {
console.log("onClick");
// !!HERE!!
};
return <button onClick={onClick}>update y</button>;
};
code:ts
// NG: 再描画される
setGlobal((currentState) => ({ ...currentState, y: "hello" }));
// NG: 何も更新してないのに再描画される
const currentState = getGlobal();
setGlobal(currentState);
// OK
setGlobal({y: "hello"});
つまり、たとえ同じ参照がsetされるとしても更新があったと判定してしまう。
前者の書き方なんかしないだろと思うかもしれない(この例だと必要ないから)
Stateが複雑な場合にimmerで更新しようとしてこの問題を踏んだ。これはState全体を受け取り、更新したいものだけを更新したオブジェクトを作って返す。なので更新されない値も保持した{ ...currentState, y: "hello" }に相当するものができてしまう。