課題
{list.map((todo, index) => (<ToDo {...todo} />))}
Reactで配列を表示する時は、子コンポーネントにキーを定義しなければ以下警告が出ます。
TodoList.js:65 Warning: Each child in a list should have a unique "key" prop.
そして、皆さんが以下の解決をしがちです。
Mapのindexをキーにします
{list.map((todo, index) => (<ToDo key={index} {...todo} />))}
->こちらは悪い習慣
そうすれば、恐らく以下のエラーが発生します。(詳細コードが下の「その他」にあります)
- 追加が正しくない
- ソートが正しくない
修正後
{list.map((todo) => (<ToDo key={todo.id} {...todo} />))}
解説
まずReact Diffing理解
コンポーネント更新される場合は、
1. 親コンポーネント変更
<div>
<Counter />
</div>
<span>
<Counter />
</span>
// 親コンポーネントをdiv->spanに変更するため。
2. コンポーネント自体に性質が変更
<div className="before" title="stuff" />
<div className="after" title="stuff" />
//className 変更
Demoを分析
KeyにMapのindex値を利用
<input/>
にはMapのindex値が変更されず、<input/>
も更新されません。
一方、変更されするコンポネートは idのlabelとcreatedAtのlabelです。
上記のデモにこれらの位置が変更されますが、<input/>
位置が変更されません。
KeyにidのTodoを利用
ソートや追加の時に、Todoのidは変更されますので、<tr/>
が全体更新されます。
結論
・Keyが必要
・Keyの値が雄一無二
・仕方無い場合はMapのindexを利用
その他
上記のコード
import React, { useState } from "react";
// Functional ToDo Component
const ToDo = ({ id, createdAt }) => (
<tr>
<td>
<label>{id}</label>
</td>
<td>
<input />
</td>
<td>
<label>{createdAt.toTimeString()}</label>
</td>
</tr>
);
// Functional ToDoList Component
const ToDoList = () => {
const [list, setList] = useState([{ id: 1, createdAt: new Date() }]);
const [toDoCounter, setToDoCounter] = useState(1);
// Sorting by earliest date
const sortByEarliest = () => {
const sortedList = [...list].sort((a, b) => a.createdAt - b.createdAt);
setList(sortedList);
};
// Sorting by latest date
const sortByLatest = () => {
const sortedList = [...list].sort((a, b) => b.createdAt - a.createdAt);
setList(sortedList);
};
// Adding a new ToDo to the end of the list
const addToEnd = () => {
const newDate = new Date();
const nextId = toDoCounter + 1;
setList([...list, { id: nextId, createdAt: newDate }]);
setToDoCounter(nextId);
};
// Adding a new ToDo to the start of the list
const addToStart = () => {
const newDate = new Date();
const nextId = toDoCounter + 1;
setList([{ id: nextId, createdAt: newDate }, ...list]);
setToDoCounter(nextId);
};
return (
<div>
<code>key=id</code>
<br />
<button onClick={addToStart}>Add New to Start</button>
<button onClick={addToEnd}>Add New to End</button>
<button onClick={sortByEarliest}>Sort by Earliest</button>
<button onClick={sortByLatest}>Sort by Latest</button>
<table>
<thead>
<tr>
<th>ID</th>
<th />
<th>Created At</th>
</tr>
</thead>
<tbody>
{list.map((todo,index ) => (
<ToDo key={index} {...todo} />
))}
</tbody>
</table>
</div>
);
};
export default ToDoList;
参考
https://www.geeksforgeeks.org/what-is-diffing-algorithm/
https://www.geeksforgeeks.org/explain-dom-diffing/?ref=oin_asr1
最後まで読んで頂いて有り難うございます。
役に立つを感じしたらハートやコメントやを残ってください