[React]コンポーネントにkeyを付与する意味

Reactコンポーネントのkey、何となくでつけていたので整理をしました。

目次

Keyを付与する意味

Reactのコンポーネントには、こんな形でkeyを付与します。
ここでは、KeyValuePairのオブジェクトをmapで回した時にkeyを付与しています。

<GridLayout>
  {props.historyList.map(d => {
    return (
      <Item href={`/history/${d.history_tag_key}`} key={d.history_tag_key} className='GridItem'>
        <ItemTitle>{d.history_tag}</ItemTitle>
      </Item>
    )
  })}
</GridLayout>

ここで、コンポーネントに対して、keyを付与しないと、eslintでエラーが出てきます。

<GridLayout>
  {props.historyList.map(d => {
    return (
      <Item href={`/history/${d.history_tag_key}`} className='GridItem'>
        <ItemTitle>{d.history_tag}</ItemTitle>
      </Item>
    )
  })}
</GridLayout>

key属性、つけてねと言われます。

Missing "key" prop for element in iterator

単純に、一意となるkeyを付与することでeslintエラーは解消されますが、
ここでkeyを付与する意味は、コンポーネントのレンダリング管理をするためです。

mapで回しているオブジェクトの更新に合わせて、keyが変更されたタイミングでコンポーネントが再描画されます。
そのため、keyには一意な値を付与するのですが、レンダリングを管理するために、インデックスや、ランダムな値は極力避けた方がよさそうです。

keyの変更でコンポーネントのstateをリセットする

コンポーネントでstateを管理していた時に、ページ遷移してもstateがリセットされない。
と言うことがありました。

例えば、userIdが変更されるたびにstateを変更したい、といった場合、useEffectで対応はできます。

export default function ProfilePage({ userId }) {
  const [comment, setComment] = useState('');

  // Avoid: Resetting state on prop change in an Effect
  useEffect(() => {
    setComment('');
  }, [userId]);
  // ...
}

しかし、これは非効率で、ProfilePage とその子コンポーネントは、まず古くなった値でレンダーされ、その後再度レンダーされるからです。

ここで利用するのはkey属性です。
コンポーネントを呼び出す時に、key属性を付与します。

export default function ProfilePage({ userId }) {
  return (
    <Profile
      userId={userId}
      key={userId}
    />
  );
}

function Profile({ userId }) {
  // This and any other state below will reset on key change automatically
  const [comment, setComment] = useState('');
  // ...
}

こうすることで、keyが変更されるたびにReact DOMを再作成します。つまり、コンポーネント内のstateをリセットするので、userIdが変更されるたびに内部のstateがリセットされます。

より詳しい内容は公式の記載をご覧ください。
https://ja.react.dev/learn/you-might-not-need-an-effect#resetting-all-state-when-a-prop-changes

Summary

eslintのメッセージに沿ってkeyをつけてましたが、Reactの公式を読むと、よりレンダリングへの知識が深まります。

よかったらシェアしてね!
目次