Reactでkey属性にインデックスを使う際の注意点を初心者向けに解説
生徒
「Reactでリストを表示するとき、keyにindexを使ってもいいって聞いたんですが、本当に大丈夫なんですか?」
先生
「使える場合もありますが、注意しないと困った動きになることがあります。」
生徒
「画面はちゃんと表示されるのに、何が問題なんでしょうか?」
先生
「Reactの仕組みとあわせて、分かりやすく説明しますね。」
1. key属性とインデックスの基本をおさらい
Reactで配列データを画面に表示するとき、key属性が必要になります。 key属性は、それぞれの表示要素を区別するための目印です。 人が集まるイベントで、名札を付けて誰が誰か分かるようにするのと同じ考え方です。
インデックスとは、配列の順番を表す番号のことです。 最初は0、次は1というように、自動で番号が割り振られます。 map関数では、このインデックスを簡単に使えるため、初心者はkeyに指定しがちです。
2. インデックスをkeyに使った基本的な例
まずは、インデックスをkeyに使ったシンプルな例を見てみましょう。 一見すると、何も問題がないように見えます。
import React from "react";
function App() {
const fruits = ["りんご", "バナナ", "みかん"];
return (
<div>
<h1>フルーツ一覧</h1>
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
</div>
);
}
export default App;
このように、表示するだけであれば、インデックスをkeyにしても問題は起きません。 そのため、初心者のうちは「これで大丈夫」と思ってしまいがちです。
3. なぜインデックスをkeyにすると問題が起きるのか
問題が起きるのは、リストの中身が途中で変わったときです。 Reactは、keyをもとに「どの要素が変わったか」を判断しています。
インデックスは順番を表す数字なので、 リストの途中に新しい要素が入ったり、削除されたりすると、 後ろの要素の番号がすべてずれてしまいます。 その結果、Reactは別の要素だと勘違いしてしまいます。
4. インデックスkeyで起きやすいトラブル例
たとえば、入力フォーム付きのリストを考えてみましょう。 入力途中のデータが、別の行に移動してしまうことがあります。 これは、Reactがkeyを頼りに要素を管理しているためです。
import React, { useState } from "react";
function App() {
const [items, setItems] = useState(["A", "B", "C"]);
return (
<div>
<ul>
{items.map((item, index) => (
<li key={index}>
<input defaultValue={item} />
</li>
))}
</ul>
</div>
);
}
export default App;
見た目では分かりにくいですが、内部ではReactが混乱しています。 これが、インデックスをkeyに使う最大の注意点です。
5. インデックスkeyが許されるケース
すべての場合で、インデックスkeyがダメというわけではありません。 リストの順番が変わらない、追加や削除がない場合は、大きな問題になりにくいです。
たとえば、固定された説明文の一覧や、完全に静的な表示などです。 ただし、後から機能を追加する可能性がある場合は、最初から安全な方法を選ぶ方が安心です。
6. インデックスの代わりに使うべきkey
もっともおすすめなのは、idのような一意の値をkeyに使うことです。 一意とは、他と重ならないという意味です。 学生番号や会員番号を思い浮かべると分かりやすいでしょう。
import React from "react";
function App() {
const users = [
{ id: 1, name: "たろう" },
{ id: 2, name: "はなこ" },
{ id: 3, name: "じろう" }
];
return (
<div>
<h1>ユーザー一覧</h1>
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
</div>
);
}
export default App;
この方法なら、リストの順番が変わってもReactは正しく判断できます。 安定した動作につながるため、実務でもよく使われています。
7. 初心者が覚えておきたい考え方
インデックスをkeyに使うと、最初は楽に書けます。 しかし、あとで機能を追加したときに、思わぬ不具合の原因になります。
「今は大丈夫」ではなく、「あとで変わるかもしれない」と考えることが大切です。 Reactでは、keyは表示のためではなく、内部管理のための重要な情報です。 その役割を理解することで、より安全なコードが書けるようになります。
まとめ
Reactにおけるkey属性は、単なる目印ではなく、仮想DOMの差分検出において非常に重要な役割を担っています。特にリスト表示を行う際には、各要素を正しく識別するためにkeyが必要となり、その指定方法によってアプリケーションの挙動が大きく変わる可能性があります。今回の記事では、初心者がついやりがちなインデックスをkeyとして使う方法について、その利便性と注意点を丁寧に解説しました。
インデックスをkeyに使う方法は、コードがシンプルになり、すぐに実装できるというメリットがあります。しかし、これはあくまで表示が固定されている場合に限った話です。リストの並び順が変わる場合や、途中に要素の追加や削除が発生するような動的な処理では、インデックスは不安定な識別子となってしまいます。その結果、Reactが要素を正しく認識できず、入力フォームの値が別の行に移動したり、意図しない再描画が発生するなどの問題につながります。
こうした問題を避けるためには、一意の値をkeyとして使うことが重要です。例えば、データベースから取得したIDや、ユーザーごとに割り振られた識別番号などが該当します。これらの値は、リストの順番が変わっても変化しないため、Reactは要素を正確に追跡することができます。この考え方は、React開発における基本でありながら、実務でも頻繁に登場する重要なポイントです。
また、今回紹介したようなトラブルは、見た目だけでは気づきにくいという特徴があります。実際には内部で状態のズレが発生しているにもかかわらず、初期段階では問題なく動いているように見えるため、バグの発見が遅れる原因にもなります。そのため、最初から正しい設計を意識することが、後々の修正コストを抑えるうえでも非常に大切です。
Reactのkey属性は、単なる記述ルールではなく、効率的な描画処理を支える仕組みの一部です。この仕組みを理解することで、パフォーマンスの向上やバグの予防につながり、より信頼性の高いアプリケーション開発が可能になります。特にリスト操作が多いアプリケーションでは、この知識がそのまま品質に直結するため、しっかりと身につけておきたいところです。
import React, { useState } from "react";
function App() {
const [users, setUsers] = useState([
{ id: 1, name: "たろう" },
{ id: 2, name: "はなこ" },
{ id: 3, name: "じろう" }
]);
const addUser = () => {
const newUser = {
id: Date.now(),
name: "新しいユーザー"
};
setUsers([newUser, ...users]);
};
return (
<div>
<h1>ユーザー一覧</h1>
<button onClick={addUser}>追加</button>
<ul>
{users.map((user) => (
<li key={user.id}>
<input defaultValue={user.name} />
</li>
))}
</ul>
</div>
);
}
export default App;
このように、一意の値をkeyに設定することで、リストの変化にも安定して対応できるようになります。特にフォームやアニメーションを伴う処理では、この違いが顕著に現れます。最初は小さな差に見えても、規模が大きくなるほど影響が大きくなるため、基本をしっかり押さえておくことが重要です。
生徒
「インデックスをkeyに使うのは簡単でしたけど、実は危ない場面があるんですね。」
先生
「そうですね。特にリストの順番が変わるときは注意が必要です。」
生徒
「見た目は変わらなくても、中でズレが起きるのが怖いですね。」
先生
「その通りです。だからこそ、最初から一意の値を使う習慣が大切です。」
生徒
「これからはidを使うようにします。」
先生
「それが安心ですね。Reactの仕組みを理解して書くコードは、より安定したものになりますよ。」