Reactで配列を表示する方法|key属性の重要性も解説
Reactで配列データを表示する基本的な方法からkey属性の重要性まで詳しく解説。map関数の使い方やパフォーマンスを向上させる実践的なテクニック
みなさん、Reactでリストや表を作ろうとして「配列のデータってどうやって表示するの?」と悩んだことはありませんか?
Reactを学び始めたとき、データが1つなら簡単に表示できても、複数のデータを一気に表示するのは最初は戸惑いますよね。 特に「key属性って何?」「なぜ警告が出るの?」といった疑問を持つ方も多いはずです。
この記事では、Reactで配列データを上手に表示する方法を、基本から応用まで分かりやすく解説します。 初心者の方でも安心して読み進められるよう、具体的なコード例とともにお伝えしますので、ぜひ最後まで読んでみてくださいね。
配列を表示する基本的な方法
まずは、Reactで配列データを表示する基本をマスターしましょう。
map関数で配列を変換しよう
JavaScriptのmap関数を使うと、配列の各要素をReactの要素に変換できます。 簡単に言うと、「配列の中身を1つずつ取り出して、HTML要素に変える」という作業ですね。
function App() {
const fruits = ['りんご', 'バナナ', 'オレンジ', 'ぶどう'];
return (
<div>
<h1>果物リスト</h1>
<ul>
{fruits.map((fruit, index) => (
<li key={index}>{fruit}</li>
))}
</ul>
</div>
);
}
このコードを見てみましょう。
fruits.map()
の部分で、配列の中身を1つずつ取り出しています。
fruit
には「りんご」「バナナ」といった文字列が順番に入ってきます。
そして、それぞれを<li>
タグで囲んでリスト表示しているんです。
実行すると、以下のようなリストが表示されます:
- りんご
- バナナ
- オレンジ
- ぶどう
オブジェクトの配列を表示してみよう
実際の開発では、文字列だけでなくオブジェクトの配列を扱うことが多いです。 例えば、ユーザー情報のような複数の項目を持つデータですね。
function App() {
const users = [
{ id: 1, name: '田中太郎', age: 25, email: 'tanaka@example.com' },
{ id: 2, name: '佐藤花子', age: 30, email: 'sato@example.com' },
{ id: 3, name: '鈴木一郎', age: 28, email: 'suzuki@example.com' }
];
return (
<div>
<h1>ユーザー一覧</h1>
<ul>
{users.map(user => (
<li key={user.id}>
<strong>{user.name}</strong> ({user.age}歳) - {user.email}
</li>
))}
</ul>
</div>
);
}
ここでは、user.id
やuser.name
のように、オブジェクトのプロパティにアクセスしています。
<strong>
タグで名前を太字にして、見やすくしているのもポイントです。
表示結果はこんな感じになります:
- 田中太郎 (25歳) - tanaka@example.com
- 佐藤花子 (30歳) - sato@example.com
- 鈴木一郎 (28歳) - suzuki@example.com
テーブル形式で表示する方法
データが多い場合は、テーブル形式の方が見やすいこともありますよね。
function UserTable() {
const users = [
{ id: 1, name: '田中太郎', age: 25, department: '開発部' },
{ id: 2, name: '佐藤花子', age: 30, department: '営業部' },
{ id: 3, name: '鈴木一郎', age: 28, department: '企画部' }
];
return (
<table>
<thead>
<tr>
<th>ID</th>
<th>名前</th>
<th>年齢</th>
<th>部署</th>
</tr>
</thead>
<tbody>
{users.map(user => (
<tr key={user.id}>
<td>{user.id}</td>
<td>{user.name}</td>
<td>{user.age}歳</td>
<td>{user.department}</td>
</tr>
))}
</tbody>
</table>
);
}
テーブルの場合は、<tr>
(行)要素を作って、その中に<td>
(セル)を配置します。
ヘッダー部分は<thead>
で、データ部分は<tbody>
で囲むのがHTMLの正しい書き方ですね。
これで、きれいな表形式でデータを表示できます。
key属性の重要性を理解しよう
さて、ここからがとても大切な部分です。 key属性について詳しく見ていきましょう。
key属性って何のためにあるの?
key属性は、Reactが配列の要素を効率的に管理するための「目印」のようなものです。
簡単に言うと、「この要素がどれなのか」をReactに教えてあげる仕組みなんです。 人間で言えば、名前や番号みたいなものですね。
key属性がないとどうなる?
まず、key属性を使わない場合の例を見てみましょう。
// ❌ key属性がない場合(警告が発生)
function TodoList() {
const todos = [
{ id: 1, text: '買い物に行く', completed: false },
{ id: 2, text: '掃除をする', completed: true },
{ id: 3, text: '勉強する', completed: false }
];
return (
<ul>
{todos.map(todo => (
<li>{todo.text}</li> {/* Warning: key属性がない */}
))}
</ul>
);
}
このコードを実行すると、ブラウザのコンソールに警告メッセージが表示されます。 「Warning: Each child in a list should have a unique "key" prop.」といった内容ですね。
これは「リストの各要素には、ユニークなkey属性をつけてください」という意味です。
key属性を正しく設定する方法
key属性を正しく設定すると、警告が消えて動作も安定します。
// ✅ key属性を正しく設定
function TodoList() {
const todos = [
{ id: 1, text: '買い物に行く', completed: false },
{ id: 2, text: '掃除をする', completed: true },
{ id: 3, text: '勉強する', completed: false }
];
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => {/* 完了状態の切り替え */}}
/>
{todo.text}
</li>
))}
</ul>
);
}
ここではkey={todo.id}
として、各todoのIDをkeyに使っています。
IDは重複しないので、Reactが各要素を正しく識別できるんです。
key属性の重要性を実例で理解しよう
key属性がなぜ重要なのか、実際の例で見てみましょう。
function App() {
const [items, setItems] = useState([
{ id: 1, name: 'アイテム1', selected: false },
{ id: 2, name: 'アイテム2', selected: false },
{ id: 3, name: 'アイテム3', selected: false }
]);
const addItem = () => {
const newItem = {
id: Date.now(),
name: `アイテム${items.length + 1}`,
selected: false
};
setItems([newItem, ...items]); // 先頭に追加
};
const toggleItem = (id) => {
setItems(items.map(item =>
item.id === id ? { ...item, selected: !item.selected } : item
));
};
return (
<div>
<button onClick={addItem}>アイテムを追加</button>
<ul>
{items.map(item => (
<li key={item.id}>
<input
type="checkbox"
checked={item.selected}
onChange={() => toggleItem(item.id)}
/>
{item.name}
</li>
))}
</ul>
</div>
);
}
このコードでは、新しいアイテムを先頭に追加したり、チェックボックスの状態を変更したりできます。
key属性があることで、Reactは「どのアイテムが新しく追加されたのか」「どのアイテムの状態が変わったのか」を正確に把握できるんです。 もしkey属性がないと、意図しない動作を引き起こす可能性があります。
適切なkey属性の選び方
key属性には何を使えばいいのでしょうか? 正しい選び方を学んでいきましょう。
良いkey属性の例
一意のIDを使う方法が最も安全で推奨される方法です。
// ✅ 一意のIDをkeyとして使用
function ProductList({ products }) {
return (
<div>
{products.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>価格: ¥{product.price}</p>
</div>
))}
</div>
);
}
IDが数字でも文字列でも問題ありません。 重要なのは、他の要素と重複しない値であることです。
// ✅ 一意の文字列をkeyとして使用
function TagList({ tags }) {
return (
<div>
{tags.map(tag => (
<span key={tag} className="tag">
{tag}
</span>
))}
</div>
);
}
タグ名のような文字列も、重複しないなら立派なkey属性になります。
避けるべきkey属性
次に、使ってはいけないkey属性の例を見てみましょう。
// ❌ 配列のインデックスをkeyとして使用(推奨されない)
function BadExample({ items }) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{item.text}</li> // 順序が変わると問題
))}
</ul>
);
}
インデックス(配列の番号)をkeyに使うのは、基本的に避けた方がいいです。 なぜなら、配列の順序が変わったときに、Reactが混乱してしまうからです。
// ❌ ランダムな値をkeyとして使用
function BadExample({ items }) {
return (
<ul>
{items.map(item => (
<li key={Math.random()}>{item.text}</li> // 毎回異なる値になる
))}
</ul>
);
}
Math.random()
のような毎回変わる値も絶対にダメです。
Reactは「毎回全部新しい要素」と認識してしまい、パフォーマンスが大幅に悪化します。
インデックスを使っても良い場合
ただし、インデックスが問題ない場合もあります。 静的なリスト(順序が変わらないリスト)なら大丈夫です。
// ✅ 静的なリストの場合はインデックスでも問題ない
function StaticMenu() {
const menuItems = ['ホーム', 'サービス', 'お問い合わせ'];
return (
<nav>
{menuItems.map((item, index) => (
<a key={index} href={`#${item}`}>
{item}
</a>
))}
</nav>
);
}
このようなナビゲーションメニューなら、項目の順序が変わることはほとんどないので、インデックスでも問題ありません。
実践的な配列表示のテクニック
ここからは、実際の開発でよく使われる応用テクニックをご紹介します。
条件付きでリストを表示する
「完了したタスクは表示しない」といった条件付き表示は、よくある要求ですね。
function TaskList({ tasks, showCompleted }) {
const filteredTasks = showCompleted
? tasks
: tasks.filter(task => !task.completed);
return (
<div>
<h2>タスク一覧</h2>
{filteredTasks.length === 0 ? (
<p>表示するタスクがありません</p>
) : (
<ul>
{filteredTasks.map(task => (
<li key={task.id} className={task.completed ? 'completed' : ''}>
{task.title}
</li>
))}
</ul>
)}
</div>
);
}
このコードでは、まずfilter
メソッドで条件に合うタスクだけを抽出しています。
そして、表示するタスクがない場合は「表示するタスクがありません」というメッセージを表示します。
条件によってリストの中身が変わるのは、ユーザーにとってとても便利な機能ですよね。
ソート機能付きリストを作る
リストを並び替える機能も、よく求められる機能です。
function SortableUserList({ users }) {
const [sortBy, setSortBy] = useState('name');
const [sortOrder, setSortOrder] = useState('asc');
const sortedUsers = [...users].sort((a, b) => {
const aValue = a[sortBy];
const bValue = b[sortBy];
if (sortOrder === 'asc') {
return aValue > bValue ? 1 : -1;
} else {
return aValue < bValue ? 1 : -1;
}
});
return (
<div>
<div>
<label>
ソート項目:
<select value={sortBy} onChange={(e) => setSortBy(e.target.value)}>
<option value="name">名前</option>
<option value="age">年齢</option>
</select>
</label>
<label>
<input
type="checkbox"
checked={sortOrder === 'desc'}
onChange={(e) => setSortOrder(e.target.checked ? 'desc' : 'asc')}
/>
降順
</label>
</div>
<ul>
{sortedUsers.map(user => (
<li key={user.id}>
{user.name} ({user.age}歳)
</li>
))}
</ul>
</div>
);
}
ここでは、useState
でソート条件を管理しています。
sortBy
で「何でソートするか」を、sortOrder
で「昇順か降順か」を決めています。
元の配列を変更しないよう、[...users]
でコピーを作ってからソートしているのもポイントです。
検索機能付きリストを作る
ユーザーが入力した文字で絞り込む検索機能も実装してみましょう。
function SearchableList({ items }) {
const [searchTerm, setSearchTerm] = useState('');
const filteredItems = items.filter(item =>
item.name.toLowerCase().includes(searchTerm.toLowerCase())
);
return (
<div>
<input
type="text"
placeholder="検索..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<ul>
{filteredItems.map(item => (
<li key={item.id}>
{item.name}
</li>
))}
</ul>
{filteredItems.length === 0 && searchTerm && (
<p>「{searchTerm}」に該当するアイテムが見つかりません</p>
)}
</div>
);
}
toLowerCase()
を使って大文字小文字を区別しない検索にしています。
また、検索結果が0件で、かつ検索文字が入力されている場合にメッセージを表示します。
検索しながらリアルタイムで結果が変わるのは、とても気持ちいい機能ですよね。
動的な配列操作をマスターしよう
実際のアプリでは、リストにアイテムを追加したり削除したりする機能が必要です。 これらの操作を学んでいきましょう。
配列に新しい要素を追加する
まずは、新しいタスクを追加する機能から始めましょう。
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: '買い物', completed: false },
{ id: 2, text: '掃除', completed: true }
]);
const [newTodo, setNewTodo] = useState('');
const addTodo = () => {
if (newTodo.trim()) {
const newItem = {
id: Date.now(),
text: newTodo,
completed: false
};
setTodos([...todos, newItem]);
setNewTodo('');
}
};
return (
<div>
<div>
<input
type="text"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
placeholder="新しいタスク"
/>
<button onClick={addTodo}>追加</button>
</div>
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.text}
</li>
))}
</ul>
</div>
);
}
新しいアイテムを追加するときは、[...todos, newItem]
のようにスプレッド構文を使います。
これで元の配列を変更せずに、新しい配列を作成できます。
Date.now()
を使って一意のIDを生成しているのも実用的なテクニックです。
配列から要素を削除する
次は、不要になったアイテムを削除する機能です。
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: '買い物', completed: false },
{ id: 2, text: '掃除', completed: true }
]);
const deleteTodo = (id) => {
setTodos(todos.filter(todo => todo.id !== id));
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<span>{todo.text}</span>
<button onClick={() => deleteTodo(todo.id)}>
削除
</button>
</li>
))}
</ul>
);
}
削除にはfilter
メソッドを使います。
「指定されたID以外のアイテム」だけを残した新しい配列を作っているんです。
削除ボタンをクリックすると、該当するアイテムがリストから消えます。
配列の要素を更新する
最後に、既存のアイテムの内容を変更する方法を見てみましょう。
function TodoApp() {
const [todos, setTodos] = useState([
{ id: 1, text: '買い物', completed: false },
{ id: 2, text: '掃除', completed: true }
]);
const toggleTodo = (id) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, completed: !todo.completed }
: todo
));
};
const updateTodoText = (id, newText) => {
setTodos(todos.map(todo =>
todo.id === id
? { ...todo, text: newText }
: todo
));
};
return (
<ul>
{todos.map(todo => (
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
<input
type="text"
value={todo.text}
onChange={(e) => updateTodoText(todo.id, e.target.value)}
/>
</li>
))}
</ul>
);
}
更新ではmap
メソッドを使います。
該当するIDのアイテムだけを変更して、他はそのまま残します。
{ ...todo, completed: !todo.completed }
の部分は、元のオブジェクトをコピーしつつ、特定のプロパティだけを変更する書き方です。
パフォーマンスを向上させるテクニック
大量のデータを扱うときは、パフォーマンスにも気を配る必要があります。
React.memoで無駄な再レンダリングを防ぐ
React.memo
を使うと、変更がない要素の再レンダリングを防げます。
// 子コンポーネントをメモ化
const TodoItem = React.memo(({ todo, onToggle, onDelete }) => {
console.log(`TodoItem ${todo.id} がレンダリングされました`);
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>削除</button>
</li>
);
});
// 親コンポーネント
function TodoList({ todos }) {
const toggleTodo = useCallback((id) => {
// 状態更新処理
}, []);
const deleteTodo = useCallback((id) => {
// 削除処理
}, []);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={toggleTodo}
onDelete={deleteTodo}
/>
))}
</ul>
);
}
React.memo
で囲んだコンポーネントは、propsが変わったときだけ再レンダリングされます。
useCallback
と組み合わせることで、さらに効果的になります。
仮想化でさらに高速化
本当に大量のデータ(数千件以上)を扱う場合は、仮想化という技術も検討できます。
// 大量データの場合の仮想化(概念的な例)
function VirtualizedList({ items, itemHeight = 50, containerHeight = 400 }) {
const [scrollTop, setScrollTop] = useState(0);
const startIndex = Math.floor(scrollTop / itemHeight);
const endIndex = Math.min(
startIndex + Math.ceil(containerHeight / itemHeight),
items.length
);
const visibleItems = items.slice(startIndex, endIndex);
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={(e) => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: items.length * itemHeight }}>
<div style={{ transform: `translateY(${startIndex * itemHeight}px)` }}>
{visibleItems.map((item, index) => (
<div key={item.id} style={{ height: itemHeight }}>
{item.name}
</div>
))}
</div>
</div>
</div>
);
}
仮想化では、画面に表示される部分だけをレンダリングします。 これにより、10万件のデータでもスムーズに動作させることができます。
まとめ
Reactで配列を表示する方法について、基本から応用まで詳しく見てきました。
map関数を使った基本的な表示方法から、key属性の正しい使い方、実践的なテクニック、パフォーマンス最適化まで、幅広い内容をカバーしました。
特に覚えておきたいポイントは以下の通りです:
- key属性には一意の値を使う
- 配列操作はイミュータブルに行う
- 大量データの場合はパフォーマンスを考慮する
これらの知識を活用すれば、ユーザーにとって使いやすく、開発者にとって保守しやすいReactアプリケーションを作れるようになります。
まずは簡単なリスト表示から始めて、少しずつ機能を追加していってみてくださいね。 きっと「Reactって便利だな」と感じていただけると思います。