← Назад к вопросам
Как расставляешь ключи элементам?
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'ей
- Используй уникальные id — приоритет первый
- Избегай index'а — использую только для статичных списков
- Генерируй id один раз — не при каждом рендере
- Не используй Math.random() — id должен быть стабильным
- Используй составные key'и — если нужен контекст
- Никогда не вычисляй 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", это важная оптимизация, которая влияет на производительность и корректность приложения.