Reactで配列を表示する方法|key属性の重要性も解説

Reactで配列データを表示する基本的な方法からkey属性の重要性まで詳しく解説。map関数の使い方やパフォーマンスを向上させる実践的なテクニック

Learning Next 運営
29 分で読めます

みなさん、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.iduser.nameのように、オブジェクトのプロパティにアクセスしています。 <strong>タグで名前を太字にして、見やすくしているのもポイントです。

表示結果はこんな感じになります:

テーブル形式で表示する方法

データが多い場合は、テーブル形式の方が見やすいこともありますよね。

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って便利だな」と感じていただけると思います。

関連記事