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の公式を読むと、よりレンダリングへの知識が深まります。