とある開発会社での千鳥の会話
大吾「おおノブ、このまえ頼んだリストの追加機能は完成しそか!?」
ノブ「UIは完成したから、残りはロジックを入れれば完成じゃ」
大吾「UIはどんなもんになっとる!?」
ノブ「こんな感じじゃ。」
大吾「これじゃこれじゃ!!」
大吾「あとは追加ボタンを押したときにリストが追加されれば完成じゃ!」
ノブ「まあ任せとき。5分もあれば終わる」
ノブ「ちなみに、こんなもん何に使うんじゃ?」
大吾「まあまあ、そこは一旦おいとこうや」
カタカタカタカタカタカタカタカタカタカタカタカタ
ノブ「配列のstateが上手く更新できんのじゃ!」
大吾「おおっっっ!?。ちょっとノブの書いたコード見せてくれんか」
ノブ「これじゃ。何が悪いのか全くわからん」
const List: NextPage = () => {
const [list, setList] = useState<string[]>(["リスト"]);
const addValue = () => {
list.push("リスト");
setList(list);
};
return (
<Container>
<div className="todo-list">
<div className="input-container">
<button onClick={addValue}>リストを追加</button>
</div>
<ul>
{list.map((i, index) => (
<li key={index}>{i}</li>
))}
</ul>
</div>
</Container>
);
};
大吾「ほうほう。なるほどなるほど」
ノブ「試しに追加ボタンを押したところでconsole.log
を使ってみたんだがリストは追加されてるんよ」
const addValue = () => {
list.push("リスト");
console.log(list);
setList(list);
};
ノブ「何回試してもstateに反映されん(泣)」
大吾「分かった!!!」
オブジェクトを更新する際は新しい値を入れんとあかん
大吾「オブジェクトを更新する際は新しい値を入れんとあかん」
大吾「ReactフックのuseState
における更新関数にはな、現在のstate
とは異なる値を渡さんといけんのじゃ」
ノブ「どういうことじゃ!?」
大吾「Reactの公式ドキュメントを見てみよか」
現在値と同じ値で更新を行った場合、React は子のレンダーや副作用の実行を回避して処理を終了します(React は Object.is による比較アルゴリズムを使用します)。
大吾「ほな、さっきのノブのコード見てみよか」
const [list, setList] = useState<string[]>(["リスト"]);
const addValue = () => {
list.push("リスト");
setList(list);
};
大吾「これだとな。list.push("リスト");
をしてもpush
が破壊的メソットなのでuseState
で定義されているlist
の値と同じ値になってしまってるんよ」
大吾「破壊的メソットが分からなかったら、この記事を確認してみてくれ」
大吾「試しにconsole.log
で見てみよか」
大吾「分かりやすいように新しく変数newList
を定義して、list
を直接代入してpush
を使ってみてくれんか」
ノブ「なるほどぉ」
const [list, setList] = useState<string[]>(["リスト"]);
const addValue = () => {
const newList = list;
list.push("リスト"); // 現在のstateを破壊的に変えている
console.log(newList === list); // 結果、同じ値になりtrueと判定
setList(list); // 同じ値を更新しても再描画が起きない
};
ノブ「おおっ! true
と表示されたぞ」
ノブ「つまりstate
で定義したlist
に直接push
で値を追加してもと同じ値とみなされてしまうんか」
ノブ「結果として更新関数をstateの更新がされず再描画起きないということじゃな」
大吾「そうじゃ」
ノブ「じゃあ一体どうすれば変わるんか!?」
大吾「スプレット演算子を使うんじゃ」
大吾「スプレット演算子で現在のstate
をコピーして、そのコピーに新しい要素をpush
して更新関数に入れるんじゃ」
大吾「まずスプレット構文で取ったコピーが異なる配列になっているか確認してみよか。」
const addValue = () => {
const copy = [...list];
console.log(copy === list); // falseと表示される
};
ノブ「おおっ!!false
と出力されたぞ」
ノブ「つまりスプレット構文を使ってコピーを取れば違うオブジェクトになるわけじゃな」
ノブ「じゃあ、コピーした配列にpushで値を追加して更新関数を使えばいいわけか」
const [list, setList] = useState<string[]>(["リスト"]);
const addValue = () => {
const copy = [...list];
copy.push("リスト");
setList(copy);
};
ノブ「おおっ!!追加されとるぞ」
大吾「ちなみに、この書き方でも同じことをしてるから覚えとき」
const [list, setList] = useState<string[]>(["リスト"]);
const addValue = () => {
setList([...list, "リスト"]);
};
ノブ「ほな、これで実装完了じゃ。感謝せぇ」
大吾「ほぼワシの手柄やないかい!!」
最後に
大吾「他にも数多の記事を書いてるから読んでくれぇぇぇぇぇ」