初学者あるあるらしい?
前提
React 17.0.1
起こったこと
こういうコンポーネントがあって
const Blog = ({blog}) => {
return (
<li key={blog.id}>{blog.title} {blog.author}</li>
)
}
export default Blog
こういうふうに使っていました。
//(略)
const [blogs, setBlogs] = useState([])
//(中略)
const getAllBlogs = async () => {
const blogs = await blogService.getAll()
setBlogs(blogs)
}
const blogList = () => {
getAllBlogs()
return (
<ul className="blog_ul">
{blogs.map(blog =>
<Blog blog={blog} />
)}
</ul>
)
}
//(中略)
return (
<div>
{/*(略)*/}
{user && blogList()}
</div>
)
画面上は特に問題なさそうでしたが、コンソールには以下のエラーが出ていました。
index.js:formatted:1 Warning: Each child in a list should have a unique "key" prop.
修正
こちらのliタグではなく
//liタグのkeyを消した
const Blog = ({blog}) => {
return (
<li>{blog.title} {blog.author}</li>
)
}
export default Blog
こちらにkeyを持たせます。
const blogList = () => {
getAllBlogs()
return (
<ul className="blog_ul">
{blogs.map(blog =>
<Blog key={blog.id} blog={blog} />
)}
</ul>
)
}
なぜkeyがいるのか
これについてはReactのドキュメントでしっかり解説されていました。
要は、keyがあればリスト内のどの要素が変更されたのかkeyの値を使って一発で判定できるけれど、keyがなければわざわざ全部比較していかなければならないということらしいです。
配列のindexをkeyに使ってはいけない
Reactドキュメントの中で紹介されていた記事です。
例えば要素が3つの配列があったとしてindexがkeyだと以下のようなことが起きてしまいます。
1: 初期状態
<li key='0'>aaa</li>
<li key='1'>bbb</li>
<li key='2'>ccc</li>
2: keyが0のものに変更を加えて、さらに要素を先頭に追加すると...
2-1:先頭(=keyが0)のものに変更を加える
A <li key='0'>MODIFIED</li>
B <li key='1'>bbb</li>
C <li key='2'>ccc</li>
2-2: 要素をkeyの値で判定しているので、先頭に要素が追加された(=追加された要素のindexは0)ことにより間違ったデータが表示されてしまう
D <li key='0'>MODIFIED</li>
A <li key='1'>bbb</li>
B <li key='2'>ccc</li>
C <li key='3'></li>
配列のindexは要素の追加や削除によって変わってしまうので、こんなふうにズレてしまうということらしいです。
なのでkeyにはユニークな値を使うべきであり、例えばDBからユーザーのデータを取ってきて一覧表示などする場合は、ユーザーのidなどをkeyにするのがベストと言えます。