【React】map()関数のkeyは本当に重要ですよ！



## はじめに
Reactのコードを書いて動的なリストをmap()関数で表現するとき、下記のようなエラーが開発者ツールに出ることがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/ea79b885-757c-8c92-e495-0d2d63d86630.png)
内容を見ますと、`リスト内の各childはユニークなkeyを持つべきだ。`と書いてますね。
keyを入力しなくても問題なく動きますので、無視したり、気付けないことも場合もあると思います。
でも、keyを使わないと何かしら問題があるから、このエラーを出しているでしょうね。
なので、今回はkeyを使わないと何が起こるかについて説明したいと思います。


## keyがないと何が起こるのか。

まず下記の簡単なinput要素をmap（）関数で表すコードを書いてみました。

```react
import { useState } from 'react'

const App = () => {
  const [inputValue, setInputValue] = useState('')
  const [list, setList] = useState([])

  const addToList = () => {
    setList((prevList) => {
      return [{ value: inputValue }, ...prevList]
    })
    setInputValue('')
  }

  return (
    <>
      <input
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <button onClick={addToList}>追加</button>
      <ul>
        {list.map((item) => {
          return <li>{item.value}</li>
        })}
      </ul>
    </>
  )
}

export default App

```

inputされた値は下記のコードによって、`<li>値</li>`になり、リストに追加されます。

```react
   <ul>
     {list.map((item) => {
       return <li>{item.value}</li>
     })}
   </ul>
```

でも、list要素を追加する際に、右側のhtmlタグを見ますとすべての`liタグ`が光ってしまいます。
![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/685ec7c0-f207-2a6e-bfc4-e272f48704ed.gif)
え？どういうこと？
すみません。説明不足でした。

Reactは画面の要素の変更前と変更後の差を比較して、変わった要素だけを変更してくれるので、非常に効率的ですが、
keyを入力しないと、変更前と変更後の比較ができない、つまり何がアップデートされた項目かがわからないので、すべての`liタグ`の要素をDOM上にアップデートしてしまうのです。

例え1000個のリスト項目があるとしたら、1個のリストを追加しただけで、1000個のリストを新たにアップデートしてしまうので、非常に効率の悪い作業になります。


### keyにvalueを入れると
エラーになることを確認して、適当にkeyにvalueの値を入れてしまうこともあると思います。

```react
   <ul>
     {list.map((item) => {
       return <li key={item.value}>{item.value}</li>
     })}
   </ul>
```
その時は、下のように、追加される要素だけが光るので、既存の要素はDOMにそのままの状態で新しい要素だけが追加されることがわかります。

![ezgif.com-video-to-gif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/babfd4c6-1f1d-9905-66e7-306b30b2ff91.gif)

ですが、valueの値が重複してしまうと、keyの値も重複してしますので、挙動が変になる原因になります。
`オレンジ`項目があるのに、もう1つの`オレンジ`を入力すると、`オレンジ`をkeyとして持っている要素が2つになるため、2つの要素が光ってしまします。
これは、要素の削除や編集時に予想できない＆エラーもでない不具合の原因になりますので注意が必要です。

![ezgif.com-video-to-gif (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/fa393d8f-3cf5-1f2c-a6f2-e936182d64d9.gif)

### keyが重複するとき
こちらをご覧ください。keyが重複するとエラーをはきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/f6a50411-15fa-4e83-4dab-057cfb42bc68.png)

内容としては、`バナナというkeyはユニークではないといけない。そうではないと、複製されたり、アップデートに不具合がある可能性がある。`になります。

下の動画を見ますと、`みかん`を入力した際に、`バナナ`の項目もう1つ入力されてしまします。




![ezgif.com-video-to-gif (5).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/65ce4a9d-7b6f-8320-4e22-8bc32c7be591.gif)


### keyにindexを入れると

よくあるミスが、keyにmap（）関数の第2引数のindexを入れることです。

```react
   <ul>
     {list.map((item, index) => {
       return <li key={index}>{item.value}</li>
     })}
   </ul>
```

せっかくkeyを追加したのに、すべての`liタグ`が光りますね。

![ezgif.com-video-to-gif (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2350392/5df9551d-8f81-fd69-b17e-e0d9ac584d10.gif)

indexは順番によって、値が変わるので、
下記の状態では、
- りんご (index:0)
- バナナ (index:1)

になり、`みかん`を追加することでindexの値が下記のように変わります。

- みかん(index:0)
- りんご (index:1)
- バナナ (index:2)
 
つまり、keyにindexを入れてしまうと、要素の追加及び削除の際にindexの値が変わるのでkeyの値も変わることで、
Reactはどの要素が既存の要素かがわからなくなり（すべての要素が変更したと認識）、すべての要素がアップデートされてしまいます。
公式文書でもできる限りindexをkeyの値として使わないことを推薦すると書いています。

## 解決策

解決策としては、ユニークな値をkeyの値として入れてあげることです。
サーバーサイドからデータを持ってくる場合はidの値が存在することが多いので便利でしょう。
そうではない場合は、ライブラリのuuidをimportしてkeyの値として入れてあげる方法があります。

https://github.com/uuidjs/uuid

## 終わりに

簡単な内容でしたが、結構性能のためには重要な知識だと思いますので、ぜひ覚えていただきたいです。
以上です。ありがとうございました。