[ Warning: Each child in a list should have a unique "key" prop.]を 解決する時、悪い習慣を注意

# 課題

```jsx
{list.map((todo, index) => (<ToDo  {...todo} />))}
```

Reactで配列を表示する時は、子コンポーネントにキーを定義しなければ以下警告が出ます。

:::note warn
TodoList.js:65 Warning: Each child in a list should have a unique "key" prop.
:::

そして、皆さんが以下の解決をしがちです。
Mapのindexをキーにします

```jsx
{list.map((todo, index) => (<ToDo key={index} {...todo} />))}
```
**->こちらは悪い習慣**


そうすれば、恐らく以下のエラーが発生します。（詳細コードが下の「その他」にあります）
+ 追加が正しくない
+ ソートが正しくない

![ScreenRecording2024-10-14at17.50.25-ezgif.com-video-to-gif-converter.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/9c31170e-c81b-c73e-3d65-2c6daeba21c5.gif)



##### 修正後

```jsx
{list.map((todo) => (<ToDo key={todo.id} {...todo} />))}
```


![oke.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/8a821906-0d9f-b423-8bce-108d298803a1.gif)


## 解説

#### まずReact Diffing理解
コンポーネント更新される場合は、
１. 親コンポーネント変更
```jsx
<div>
  <Counter />
</div>
 
<span>
  <Counter />
</span>

// 親コンポーネントをdiv->spanに変更するため。
```

２. コンポーネント自体に性質が変更
```jsx
<div className="before" title="stuff" />

<div className="after" title="stuff" />

//className 変更
```
### Demoを分析

##### KeyにMapのindex値を利用 

```<input/> ```にはMapのindex値が変更されず、```<input/> ``` も更新されません。
一方、変更されするコンポネートは　idのlabelとcreatedAtのlabelです。
上記のデモにこれらの位置が変更されますが、```<input/> ``` 位置が変更されません。

![Screenshot 2024-10-14 at 17.22.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/efe94b6c-0315-9b55-c5fe-cb335fea57c4.png)
（補足：更新のものが緑色）

##### KeyにidのTodoを利用 

ソートや追加の時に、Todoのidは変更されますので、```<tr/>```が全体更新されます。

![Screenshot 2024-10-14 at 17.32.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703979/811e2efb-3a5b-21f9-74d7-44d0b996eb20.png)




# 結論

・Keyが必要
・Keyの値が雄一無二
・仕方無い場合はMapのindexを利用

# その他

上記のコード
```jsx
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

最後まで読んで頂いて有り難うございます。:bow:
役に立つを感じしたらハートやコメントやを残ってください:bow: