4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【React】配列のkeyにインデックスを使うのは辞めようねというお話

Last updated at Posted at 2025-02-03

初めに

React でリストをレンダリングするときに、map を使って key を指定することは必須です。

key の指定方法として インデックスを使う方法 をよくUdemy講座や初心者向けの解説記事などで散見します。

私も最初はindexを利用したコーディングをしていたのですが、
勉強していく中でそれは思わぬバグを踏む可能性や効率的なレンダリングではないことに気づいたので記事にします。

実際に React の公式ドキュメントでも key には一意の識別子を使うことを推奨していますが、なぜインデックスを key に使うのが問題になるのでしょうか?

▼以下参考

この記事では、以下の流れで key に関する問題を詳しく解説し、適切な方法を紹介していきます。

記事の流れ

1.keyを設定しないと何が問題なのか?
2. key にインデックスを使うと何が問題なのか?
3. ではどうすればよいのか?
4. まとめ

各コードの実行結果のスクリーンショットや、コンソールでのレンダリング状況も掲載するので、実際にどのような挙動になるのかも分かりやすく解説します。
また、そもそもkeyなんて知らないよという方も読んで雰囲気がわかるようにはなっているので、Reactを触り始めた方にはぜひ読んでほしいです。

それでは、まず key にインデックスを使うと何が問題になるのかを見ていきましょう!

key を設定しないと何が問題なのか?

React では、リストをレンダリングするときに key を指定しないと、要素の差分を適切に管理できず、不要な再レンダリングが発生することがあります。

以下のコードを見てみましょう。

import { useState } from 'react';
import './App.css';

function App() {
  const [tasks, setTasks] = useState(["タスク1", "タスク2", "タスク3"]);
  const [newTask, setNewTask] = useState("");

  const addTask = () => {
    if (newTask.trim() === "") return;
    setTasks([...tasks, newTask]);
    setNewTask("");
  };

  return (
    <>
      <h1>タスク管理アプリ</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="新しいタスクを入力"
      />
      <button onClick={addTask}>追加</button>
      <ul>
        {tasks.map((task) => (
          <li>{task}</li>
        ))}
      </ul>
    </>
  );
}

export default App;

このコードでは key を指定していません。そのため、リストの更新時に React はどの要素が新しく追加されたのかを正しく識別できず、不要な再レンダリングが発生する可能性があります。

実行画面

image.png

コンソールの様子

image.png

keyにはしっかりとユニークなIDを追加しようね
と怒られていますね

次に、key にインデックスを使った場合にどのような問題が起こるのかを見ていきましょう。

key にインデックスを使うと何が問題なのか?

いったんの解決策初心者向け説明でkeyにindexを設定しているコードを散見します。
key にインデックスを指定することは一見簡単な解決策に思えます。
しかし、実際には以下のような問題が発生します。

コード例

import { useState } from 'react';
import './App.css';

function App() {
  const [tasks, setTasks] = useState(["タスク1", "タスク2", "タスク3"]);
  const [newTask, setNewTask] = useState("");

  const addTask = () => {
    if (newTask.trim() === "") return;
    setTasks([...tasks, newTask]);
    setNewTask("");
  };

  return (
    <>
      <h1>タスク管理アプリ</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="新しいタスクを入力"
      />
      <button onClick={addTask}>追加</button>
      <ul>
        {tasks.map((task, index) => (
          <li key={index}>{task}</li>
        ))}
      </ul>
    </>
  );
}

export default App;

key にインデックスを使うと発生する問題

  1. リストの順番が変わると、React が正しく識別できない

    • 並び替えが発生すると、React が要素を誤認識し、意図しないレンダリングが起こることがあります。
  2. 新しい要素が追加・削除されると、不要な再レンダリングが発生する

    • 先頭に新しい要素を追加すると、すべてのインデックスが変わるため、React が正しく認識できずに過剰な更新が発生します。

実行画面とコンソールの様子

Videotogif (2).gif

コンソールの挙動の原因

追加前:

0 → "タスク1"
1 → "タスク2"
2 → "タスク3"

初期で上のようなリストが、追加をすることで以下のようになります。

追加後:

0 → "新しいタスク" (本来のタスク1の `key=0` に変更)
1 → "タスク1" (本来のタスク2の `key=1` に変更)
2 → "タスク2" (本来のタスク3の `key=2` に変更)
3 → "タスク3"

このように、既存のすべての key がズレてしまい、React が「リストのすべての要素が変わった」と誤認識してしまいます。
reactの強みである仮想DOMによる差分だけのレンダリングが上手に機能していませんね。

ではどうすればよいのか?

key にインデックスを使うと問題が発生するため、リストの各要素にユニークな識別子(ID)を key に設定する ことが推奨されます。

適切な key の設定方法

import { useState } from 'react';

function App() {
  const [tasks, setTasks] = useState([
    { id: 1, name: "掃除" },
    { id: 2, name: "洗濯" },
    { id: 3, name: "料理" }
  ]);
  const [newTask, setNewTask] = useState("");

  const addTask = () => {
    if (newTask.trim() === "") return;
    const newTaskObj = { id: Date.now(), name: newTask };
    setTasks([newTaskObj, ...tasks]);
    setNewTask("");
  };

  return (
    <>
      <h1>タスク管理アプリ</h1>
      <input
        type="text"
        value={newTask}
        onChange={(e) => setNewTask(e.target.value)}
        placeholder="新しいタスクを入力"
      />
      <button onClick={addTask}>追加</button>
      <ul>
        {tasks.map((task) => (
          <li key={task.id}>{task.name}</li>
        ))}
      </ul>
    </>
  );
}

export default App;

実行画面とコンソールの様子

▼しっかりと追加した分だけ追加でレンダリングされていますね
Videotogif (5).gif

なぜこのような書き方が良いか?

  1. 要素が並び替えられても key が変わらないため、React が適切に差分を認識できる
  2. 要素を追加・削除しても、既存の要素の key が変わらず、不要な再レンダリングを防げる

このように、適切な key を設定することで、React のレンダリングが最適化され、意図しない挙動を防ぐことができます。

まとめ

key を設定しないと、React が要素の変更を適切に判断できず、不要な再レンダリングが発生する。

key にインデックスを使用すると、リストの並び替えや要素の追加・削除時に key がずれてしまい、意図しないレンダリングが起こる。

最適な key 設定は、ユニークな識別子(ID)を使用すること。

key を適切に設定することで、React の仮想DOMが最大限活かされ、効率的なレンダリングが実現できる。

React でリストを扱う際には、常に key の適切な指定を意識し、最適なパフォーマンスを確保しましょう!

まとめ

  • key を設定しないと、React が要素の変更を適切に判断できず、不要な再レンダリングが発生する。
  • key にインデックスを使用すると、リストの並び替えや要素の追加・削除時に key がずれてしまい、意図しないレンダリングが起こる。
  • 最適な key 設定は、ユニークな識別子(ID)を使用すること
  • key を適切に設定することで、React の仮想DOMが最大限活かされ、効率的なレンダリングが実現できる。

React でリストを扱う際には、常に key の適切な指定を意識し、最適なパフォーマンスを確保しましょう!

🎉 JISOUのメンバー募集中! 🎉

プログラミングコーチング JISOU では、新たなメンバーを募集しています。
日本一のアウトプットコミュニティで一緒に学び、成長し、キャリアアップを目指しませんか?💻✨


🌟 JISOUで得られること

入ったばかりですが、本当に多くのことを学べます

  • 実践的なアウトプットで確実なスキルアップ
  • プロのコーチによるマンツーマン指導
  • 成長意欲の高い仲間と切磋琢磨できる環境
  • キャリアアップに直結する実践的なアドバイス

📢 詳細はこちら

興味のある方は、ぜひ公式ホームページをチェックしてください👇
🔗 JISOU公式ホームページ


一緒に日本一のアウトプットコミュニティを作り上げましょう!🎯

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?