はじめに
Reactで配列からmapメソッドを使ってJSXでリスト要素を作成したときにkeyをつけ忘れてwarningで怒られてつけるということよくあるのではないでしょうか?
Warning: Each child in a list should have a unique "key" prop.
リスト要素にはkeyを渡そうねとか、そのときmapのindex使うのはよくないよ、という記事はよく見かけますが、今回はReactの公式ドキュメントを読んでいてリスト要素以外でもkeyが活かせるパターンを見つけたので紹介します。
こちらの章ではそれuseEffect使う必要ないんじゃない?という例をいろいろと紹介しているのですが、その中で、渡ってくるpropsが変わるたびにstateを初期化したい場合はuseEffect使わずにkey属性が使えるよ、という話がありました。
NGな例🙅
useEffectを使って初期化する場合
const Profile = ({ userId }) => {
const [comment, setComment] = useState('');
useEffect(() => {
setComment(''); // userIdが変わるたびにcommentが空文字列になる
}, [userId]);
// ...
}
Goodな例🙆
呼び出し側でkeyにも初期化のトリガーにしたい変数を渡す
const ProfilePage = ({ userId }) => {
return (
<Profile
userId={userId}
key={userId} // これでもuserIdが変わるたびにcommentが空文字列になる
/>
);
}
const Profile = ({ userId }) => {
const [comment, setComment] = useState('');
// ...
}
仕組み
Reactでは同じコンポーネントをReactの仮想DOMツリーの同じ位置に描画する場合、そのコンポーネントのstateを保持したまま更新します。
しかし、key属性を追加してuserIdが異なるコンポーネントは異なるkeyになるようにすることで、別のコンポーネントと見なされてDOMを再生成するので、stateもリセットさせることができます。
最後に
keyはリスト要素にしか使ったことがなかったのですが、こんな使い方もあるんですね。
NG例のようなことをしている箇所を見つけたらkeyで代用できないか検討したいと思いました。
ちなみに最初に貼った公式ドキュメントのページの一番最後にReactアプリのプレイグラウンド付きのチャレンジ問題があるのですが、その3問目がちょうどこの例なので、サクッと試してみたい場合はそれを使うとよいかもしれません。