React component の Key が 鍵
component と state の関係
コンポーネントにはそれぞれステートが存在する。簡単に下記のようなコードの時、ParentComponentの中にChildrenComponentが2つ定義されているので合計3つのステートが存在することになる。
const ParentComponent = () => {
return (
<>
<ChildrenComponent />
<ChildrenComponent />
</>
)
}
当然、それぞれのChildrenComponentは独立したステートを所有しているので内部でカウントアップするような処理を行ってもそれぞれのステート内部の変数は共有されない。
つまり各コンポーネントのクリック回数は独立してカウントされる。
ではこれにさらに、トグルボタンを用意して2つのChildrenComponentの表示を片方ずつに限定して切り替えるようにしてみる。トグルによってコンポーネントが切り替えられたことが分かりやすいようにtitleというプロップスをParentComponentからChildrenComponentに渡している。
トグルボタンを押すことでtitle = Aのコンポーネントとtitle = Bのコンポーネントの表示が切り替わるはずだ。
ではいくつかカウントアップをした状態でもう一方のコンポーネントに表示を切り替えると不思議なことに値が切り替わらない。
ReactのコンポーネントのステートはDomの位置に対応してアタッチするコンポーネントを選んでいるため、title = Aのコンポーネントが存在していた箇所にtitle = Bのコンポーネントが置き換わるとステートがリセットされない仕組みになっている。
それを証明するために例えばtitle = Aのコンポーネントにのみdivタグで囲んだ上で同じ挙動をさせるとDomの構造が変わるのでステートはリセットされる。
React component の Key が 鍵
上のコードのように敢えてDom構造を変えてステートが共有されないようにするのも良いが書き方的にスマートではない。そこで2つのChildrenComponentのステートを識別させるために使用するのがKeyプロパティだ。
使い方は簡単でコンポーネントにKeyプロパティと値を設定してあげれば良いだけ。
カウントアップをいくつか行った上でコンポーネントを切り替えると期待通りにステートの値がリセットされている。
(おまけ)コンポーネントをリセットした後も以前の値を維持したい
先ほどのコードでtitle = Aのコンポーネントでいくつかカウントアップをした後にtitle = Bのコンポーネントに切り替えて、再度title = Aのコンポーネントを表示するとカウントが0になっている。
一度目のレンダリング時にカウントアップした値をtitle = Aのコンポーネントで保持したままにしたい場合、下記のようにParentComponentの中にtitle = Aのコンポーネントとtitle = Bのコンポーネントのステートを定義して、それぞれ更新していくようにする必要がある。
Discussion