はじめに
Reactではコンポーネントに対してkeyを付与できます。
<Component key={key} />
keyの型はstring | number | bigint | null | undefinedです。keyはコンポーネントがUIツリーを構築する際の識別子として役立ちます。
この記事ではkeyがどのように使われてどのような役割を持つかについて解説します。
コンポーネントを繰り返し描画するために使う
keyはコンポーネント内での繰り返し処理を記述する際に使われます。
const Sample: FC = () => {
const users = useUsers();
return (
<div>
{users.map((user) => (
<User
key={user.id}
name={user.name}
mailaddress={user.mailaddress}
/>
)}
</div>
);
};
keyはusersによって展開された複数のUserコンポーネント同士を区別するために使われます。
これによって、異なるレンダー間で展開されるコンポーネントの増減や入れ替えをReactが賢く検知して最適なDOMの更新を行うことが可能になります。
繰り返し処理で生成した複数のコンポーネントにkeyがない場合や、keyが重複している場合はコンソール上でエラーが発生します。一見問題なく動いて見えることが多いですが、思わぬバグに繋がったり、Reactが行う最適化の恩恵を受けられないので、コンポーネントにそれぞれ異なるkeyを渡すようにしましょう。
また、レンダー間で同一のコンポーネントには同じkeyを付与してください。以下のように繰り返しのインデックスを元にした値を付与したり、ランダムな値を付与したりするとReactが最適化のための行動を起こせないです。
// インデックス
{users.map((user, idx) => (
<User
key={idx}
name={user.name}
mailaddress={user.mailaddress}
/>
)}
// ランダム
{users.map((user) => (
<User
key={randomUUID()}
name={user.name}
mailaddress={user.mailaddress}
/>
)}
keyを渡すためにコンポーネントの外部で一意の値を生成するか、外部から渡って来た時に持つ位置の値(DBのプライマリーキーなど)を元に作成してください。
コンポーネントをリセットするために使う
レンダー間で異なるkeyをコンポーネントに付与することで、Reactがレンダー前後のコンポーネントを異なるものと判断し、コンポーネントのマウントをやり直します(再レンダリングではないです)。
これによって、対象のコンポーネントの状態をリセットできます。
以下はチェックボックスを切り替えると同時にコンポーネントのkeyを変更するテキストエリアと、keyを付与しないテキストエリアを表示しています。
両方のテキストエリアに文字を入力したのちに、チェックボックスを切り替えると、上のコンポーネントは状態がリセットされて何も入力されていないようになるのに対して下コンポーネントは状態の変化が起きていません。
下のコンポーネントで変化が起きていないのは、ReactはUIツリーにおいて同じ位置で同じコンポーネントがレンダーされるときに状態を引き継いてしまうからです(これは通常自然な挙動で悪い挙動では全くないです)。
このように、keyを変更することでコンポーネントの初期化を行えます。上位のコンポーネントのある状態に依存して、コンポーネントを新しく生成したい場合は状態を初期化する関数を使って手動で行うのではなく、keyを用いて行うことがおすすめです。これによって状態数が変更されることによって生じる初期化の漏れや、実質的に異なるコンポーネントであることを明確にReactに伝えられます。