Хотите заказать веб-сайт? Связаться с нами

Как React работает со списками данных и ключами

Цель урока: научиться отображать массивы данных в React: превращать список объектов в элементы интерфейса, правильно использовать ключи (key) для оптимизации производительности и эффективно управлять динамическими списками.

Как React работает со списками данных и ключами

Введение: зачем нужны списки в React

Любое современное приложение работает со списками:

  1. Список товаров в интернет-магазине
  2. Список сообщений в чате
  3. Список пользователей в админке
  4. Список задач в To-Do приложении

Главная мысль:

React не имеет специального синтаксиса для списков. Всё делается через метод массива .map(), который превращает данные в JSX-элементы.

Базовый рендеринг списка с .map()

Синтаксис:

{массив.map((элемент, индекс) => (
  <JSX-элемент key={уникальный_идентификатор}>
    {элемент}
  </JSX-элемент>
))}

Простейший пример:

function FruitList() {
  const fruits = ['Яблоко', 'Банан', 'Апельсин'];

  return (
    <ul>
      {fruits.map((fruit) => (
        <li key={fruit}>{fruit}</li>
      ))}
    </ul>
  );
}
// Результат:
// • Яблоко
// • Банан
// • Апельсин

Пример с объектами:

function UserList() {
  const users = [
    { id: 1, name: 'Алексей', age: 25 },
    { id: 2, name: 'Мария', age: 30 },
    { id: 3, name: 'Иван', age: 28 }
  ];

  return (
    <div>
      {users.map((user) => (
        <div key={user.id}>
          <h3>{user.name}</h3>
          <p>Возраст: {user.age}</p>
        </div>
      ))}
    </div>
  );
}

Что такое key и зачем он нужен

Key — это специальный атрибут, который React использует для идентификации элементов в списке.

Зачем он нужен?

  1. React отслеживает, какие элементы изменились, добавились или удалились
  2. Без key React будет пересоздавать все элементы при любом изменении
  3. С key React обновляет только те элементы, которые действительно изменились

Как это работает:

// Без key (плохо ❌)
{users.map((user) => (
  <li>{user.name}</li>
))}

// С key (хорошо ✅)
{users.map((user) => (
  <li key={user.id}>{user.name}</li>
))}

Правила выбора key

Каким должен быть key:

  1. Уникальным среди элементов одного списка
  2. Стабильным (не меняется между рендерами)
  3. Предсказуемым

Что можно использовать как key:

Вариант Пример Подходит?
ID из базы данных key={user.id} ✅ Отлично
Уникальный индекс (только если статичный список) key={index} ⚠️ С осторожностью
Уникальная строка key={email} ✅ Можно
Случайное число key={Math.random()} ❌ Никогда

Почему index — не всегда хороший выбор:

// Проблема с index
const todos = ['Купить хлеб', 'Позвонить маме', 'Сделать уроки'];
{todos.map((todo, index) => (
  <li key={index}>{todo}</li>
))}
// Если удалить первый элемент, остальные получат новые индексы
// React пересоздаст их все, а не обновит
Из ноутбука появляются летающие светящиеся экраны

Оживи свой сайт. Освой JavaScript!

Статичная верстка — это только скелет. Наш онлайн-курс "JavaScript с нуля до профи" даст твоим страницам мышцы и нервы. Научись создавать слайдеры, формы, интерактивные карты и получать данные с сервера.

От теории — к реальным скриптам в твоём портфолио.

Подробнее о курсе

Пример с динамическим списком (To-Do)

Полноценный пример работы со списком:

import { useState } from 'react';

function TodoList() {
  const [tasks, setTasks] = useState([
    { id: 1, text: 'Купить хлеб', done: false },
    { id: 2, text: 'Позвонить маме', done: true },
    { id: 3, text: 'Сделать уроки', done: false }
  ]);

  const [newTask, setNewTask] = useState('');

  // Добавление задачи
  const addTask = () => {
    if (newTask.trim() === '') return;
    
    const newTaskObj = {
      id: Date.now(), // Уникальный ID
      text: newTask,
      done: false
    };
    
    setTasks([...tasks, newTaskObj]);
    setNewTask('');
  };

  // Удаление задачи
  const deleteTask = (id) => {
    setTasks(tasks.filter(task => task.id !== id));
  };

  // Переключение статуса
  const toggleTask = (id) => {
    setTasks(tasks.map(task => 
      task.id === id ? { ...task, done: !task.done } : task
    ));
  };

  return (
    <div>
      <h2>Мои задачи</h2>
      
      <input 
        value={newTask} 
        onChange={(e) => setNewTask(e.target.value)} 
        placeholder="Новая задача..."
      />
      <button onClick={addTask}>Добавить</button>

      <ul>
        {tasks.map((task) => (
          <li key={task.id} style={{ 
            textDecoration: task.done ? 'line-through' : 'none' 
          }}>
            <span onClick={() => toggleTask(task.id)}>
              {task.text}
            </span>
            <button onClick={() => deleteTask(task.id)}>✕</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

Работа с пустыми списками

Всегда обрабатывай случай, когда список пуст:

function ProductList({ products }) {
  // Проверка на пустой массив
  if (products.length === 0) {
    return <p>Товаров пока нет</p>;
  }

  return (
    <div>
      {products.map((product) => (
        <div key={product.id}>
          <h3>{product.name}</h3>
          <p>Цена: {product.price} ₽</p>
        </div>
      ))}
    </div>
  );
}

Лучше с использованием тернарного оператора:

function ProductList({ products }) {
  return (
    <div>
      {products.length === 0 ? (
        <p>Товаров пока нет</p>
      ) : (
        products.map((product) => (
          <div key={product.id}>
            <h3>{product.name}</h3>
            <p>Цена: {product.price} ₽</p>
          </div>
        ))
      )}
    </div>
  );
}

Фильтрация и сортировка списков

Фильтрация перед рендерингом:

function UserList({ users, filter }) {
  const filteredUsers = users.filter(user => 
    user.name.toLowerCase().includes(filter.toLowerCase())
  );

  return (
    <div>
      {filteredUsers.map((user) => (
        <div key={user.id}>{user.name}</div>
      ))}
    </div>
  );
}

Сортировка перед рендерингом:

function SortedProducts({ products }) {
  const sortedProducts = [...products].sort((a, b) => a.price - b.price);

  return (
    <div>
      {sortedProducts.map((product) => (
        <div key={product.id}>
          {product.name} - {product.price} ₽
        </div>
      ))}
    </div>
  );
}

key вложенных списков

Если у тебя вложенные списки, каждый должен иметь свой уникальный key:

function CategoryList({ categories }) {
  return (
    <div>
      {categories.map((category) => (
        <div key={category.id}>
          <h3>{category.name}</h3>
          <ul>
            {category.items.map((item) => (
              <li key={item.id}>{item.name}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}

Важно: key должен быть уникальным внутри одного уровня. То есть элементы в разных категориях могут иметь одинаковые id.

key и производительность

Когда React использует key:

  1. При добавлении нового элемента в конец списка
  2. При удалении элемента
  3. При изменении порядка элементов

React с key:

// Добавляем элемент в конец
// React создаст только 1 новый элемент
const newList = [...list, newItem];

React без с key:

// Добавляем элемент в конец
// React создаст все элементы заново
const newList = [...list, newItem];

Альтернативы .map() для списков

Object.keys() для объектов:

const user = {
  name: 'Алексей',
  age: 25,
  city: 'Москва'
};

return (
  <div>
    {Object.keys(user).map((key) => (
      <p key={key}>{key}: {user[key]}</p>
    ))}
  </div>
);

Object.entries() для ключ-значение:

{Object.entries(user).map(([key, value]) => (
  <p key={key}>{key}: {value}</p>
))}

Частые ошибки новичков

Ошибка Почему Исправление
Нет key React ругается в консоли Добавь key={user.id}
Использование index как key Проблемы при сортировке/удалении Используй уникальный ID
key={Math.random()} Меняется при каждом рендере Используй стабильный ID
Одинаковые key в списке React путает элементы Проверь уникальность
Забыл .map() вернуть JSX Ошибка рендеринга Убедись, что есть return

Итог (что мы узнали)

Что узнали Как это работает
Рендеринг списков Используем .map() для превращения данных в JSX
key Уникальный идентификатор для каждого элемента
Правила key Уникальный, стабильный, предсказуемый
Пустые списки Обрабатываем через length === 0
Фильтрация Фильтруем перед .map()
Сортировка Сортируем перед .map()
Вложенные списки Каждый уровень со своим key

Задание для закрепления (на 1–2 часа)

Создай компонент EmployeeList, который:

  1. Хранит список сотрудников в state (имя, должность, отдел, id)
  2. Отображает их в виде списка/таблицы с key={employee.id}
  3. Добавляет нового сотрудника через форму (имя, должность, отдел)
  4. Удаляет сотрудника по кнопке «Удалить»
  5. Фильтрует по отделу (выпадающий список: Все, Отдел 1, Отдел 2, Отдел 3)
  6. Сортирует по имени (кнопка «Сортировать по имени»)

Дополнительно: Добавь поиск по имени (input). Если список пуст — показывай «Сотрудники не найдены».

Проверка: Проверь, что при добавлении/удалении React правильно обновляет список и не пересоздаёт все элементы.

Пример выполнения задания

Что дальше?

В следующем уроке разберём Composition vs Inheritance — как строить сложные интерфейсы из маленьких компонентов. Ты узнаешь, почему React предпочитает композицию (сборку из частей) вместо наследования (классов), и научишься создавать гибкие и переиспользуемые компоненты. Это изменит твой подход к проектированию приложений.

Теги: