← Назад к вопросам

Как расставляешь ключи элементам?

2.0 Middle🔥 171 комментариев
#Soft Skills и рабочие процессы

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Key prop в React

Key — это специальный prop в React, который помогает идентифицировать элементы списка и оптимизировать переведение (reconciliation). Правильное использование key критично для производительности и корректности.

Почему нужны key'и?

// Пример проблемы без key'я
export function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React' },
    { id: 2, text: 'Build app' }
  ]);

  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>  // ПЛОХО! Используем index как key
          <input type="checkbox" defaultChecked={false} />
          {todo.text}
        </li>
      ))}
    </ul>
  );
}

// Проблема: если удалим первый элемент, индексы изменятся:
// До:   [id:1, index:0], [id:2, index:1]
// После: [id:2, index:0]  <- DOM переиспользует старый элемент!
// Это может привести к потере состояния input'а

Правильное использование key'я

// Правильный способ - использовать уникальный идентификатор
export function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: 'Learn React' },
    { id: 2, text: 'Build app' }
  ]);

  const handleDelete = (id) => {
    setTodos(todos.filter(todo => todo.id !== id));
  };

  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>  // ХОРОШО! Используем уникальный id
          <input type="checkbox" defaultChecked={false} />
          {todo.text}
          <button onClick={() => handleDelete(todo.id)}>Delete</button>
        </li>
      ))}
    </ul>
  );
}

// Теперь React знает, какой элемент удален:
// До:   [id:1], [id:2]
// После: [id:2]  <- React удаляет DOM элемент с key="1"

Когда использовать index как key

// Хорошо использовать index ТОЛЬКО если:
// 1. Список статичный (нет добавления/удаления)
// 2. Нет фильтрации
// 3. Нет сортировки

// Пример: список не меняется
export function StaticList() {
  const items = ['Apple', 'Banana', 'Cherry'];

  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{item}</li>  // OK - список статичный
      ))}
    </ul>
  );
}

Использование key с компонентами

// Пример 1: Список компонентов с key
export function UserList() {
  const [users, setUsers] = useState([
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' }
  ]);

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

// Пример 2: Дочерний компонент с состоянием
export function UserCard({ user }) {
  const [isExpanded, setIsExpanded] = useState(false);

  return (
    <div>
      <h3>{user.name}</h3>
      <button onClick={() => setIsExpanded(!isExpanded)}>
        {isExpanded ? 'Collapse' : 'Expand'}
      </button>
      {isExpanded && <p>Details...</p>}
    </div>
  );
}

// Без правильного key, при изменении списка состояние может "прилипнуть"
// к элементу с другим id'ом

Генерирование уникальных key'ей

// Способ 1: Используем существующий уникальный id
export function Products() {
  const [products, setProducts] = useState([
    { id: 'prod-1', name: 'Laptop' },
    { id: 'prod-2', name: 'Mouse' }
  ]);

  return (
    <div>
      {products.map((product) => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  );
}

// Способ 2: UUID для новых элементов
import { v4 as uuidv4 } from 'uuid';

export function AddItem() {
  const [items, setItems] = useState([]);

  const handleAdd = (text) => {
    setItems([...items, {
      id: uuidv4(),  // Генерируем уникальный id
      text,
      createdAt: Date.now()
    }]);
  };

  return (
    <>
      <button onClick={() => handleAdd('New item')}>Add</button>
      <ul>
        {items.map((item) => (
          <li key={item.id}>{item.text}</li>
        ))}
      </ul>
    </>
  );
}

// Способ 3: Комбинация нескольких свойств
export function CommentsList({ postId, comments }) {
  return (
    <ul>
      {comments.map((comment) => (
        <li key={`${postId}-${comment.id}`}>
          {comment.text}
        </li>
      ))}
    </ul>
  );
}

Частые ошибки с key'ями

// ПЛОХО 1: Генерирование key'я при каждом рендере
export function Bad1() {
  return (
    <ul>
      {items.map((item) => (
        <li key={Math.random()}>{item.text}</li>  // Новый key каждый раз!
      ))}
    </ul>
  );
}

// ПЛОХО 2: Использование нестабильного индекса
export function Bad3() {
  const [filteredItems, setFilteredItems] = useState([]);

  return (
    <ul>
      {filteredItems.map((item, index) => (
        <li key={index}>{item}</li>  // Индекс меняется при фильтрации
      ))}
    </ul>
  );
}

// ХОРОШО: Используем стабильный уникальный идентификатор
export function Good() {
  const items = [
    { id: 'unique-1', text: 'Item 1' },
    { id: 'unique-2', text: 'Item 2' }
  ];

  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  );
}

Key и условный рендер

// Пример: key помогает при условном рендере
export function ConditionalRender() {
  const [showDetails, setShowDetails] = useState(false);
  const items = [
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob', email: 'bob@example.com' }
  ];

  return (
    <>
      <button onClick={() => setShowDetails(!showDetails)}>
        Toggle Details
      </button>
      <ul>
        {items.map((item) => (
          <li key={item.id}>
            <div>{item.name}</div>
            {showDetails && <div>{item.email}</div>}
          </li>
        ))}
      </ul>
    </>
  );
}

// Правильный key гарантирует, что React не потеряет элемент при условном рендере

Best practices для key'ей

  1. Используй уникальные id — приоритет первый
  2. Избегай index'а — использую только для статичных списков
  3. Генерируй id один раз — не при каждом рендере
  4. Не используй Math.random() — id должен быть стабильным
  5. Используй составные key'и — если нужен контекст
  6. Никогда не вычисляй key — он должен быть констант или взят из data
// Итоговый правильный паттерн:
export function ProperKeyUsage() {
  const [items, setItems] = useState([
    { id: 1, text: 'First' },
    { id: 2, text: 'Second' }
  ]);

  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.text}</li>
      ))}
    </ul>
  );
}

Key'и — это не просто "требование React", это важная оптимизация, которая влияет на производительность и корректность приложения.

Как расставляешь ключи элементам? | PrepBro