← Назад к вопросам
Для чего React просит указывать ключ при рендере массива?
2.0 Middle🔥 191 комментариев
#React#Архитектура и паттерны
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Для чего React просит указывать ключ при рендере массива?
Ключ (key) в React — это критично важный аспект оптимизации производительности и сохранения состояния компонентов. Это одна из наиболее частых ошибок, которые делают разработчики.
Что такое ключ?
Ключ — это уникальный идентификатор элемента в списке, который помогает React определить, какой элемент изменился, был добавлен или удален.
// НЕПРАВИЛЬНО - без ключа
{items.map((item) => (
<div>{item.name}</div>
))}
// ПРАВИЛЬНО - с ключом
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
Почему это важно?
1. Сохранение состояния компонентов
function ListItem({ item }) {
const [checked, setChecked] = useState(false);
return (
<div>
<input
type="checkbox"
checked={checked}
onChange={(e) => setChecked(e.target.checked)}
/>
{item.name}
</div>
);
}
function List() {
const [items, setItems] = useState([
{ id: 1, name: 'Элемент 1' },
{ id: 2, name: 'Элемент 2' },
{ id: 3, name: 'Элемент 3' }
]);
return (
<div>
{items.map((item) => (
// НЕПРАВИЛЬНО - key={index}
<ListItem key={item.id} item={item} />
))}
</div>
);
}
Проблема без правильного ключа:
- Пользователь чекает первый элемент
- Мы удаляем первый элемент из массива
- React думает, что это просто переиндексирование
- State остается, но применяется к другому элементу!
- Второй элемент вдруг окажется checked!
2. Производительность
// ПЛОХО - key={index}
function UserList() {
const [users, setUsers] = useState([
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' },
{ id: 3, name: 'Петр' }
]);
const addUserAtStart = () => {
setUsers([{ id: 0, name: 'Новый' }, ...users]);
};
return (
<div>
<button onClick={addUserAtStart}>Добавить в начало</button>
{users.map((user, index) => (
// key={index} НЕПРАВИЛЬНО!
<UserProfile key={index} user={user} />
))}
</div>
);
}
// Что происходит:
// Было: key=0 (Иван), key=1 (Мария), key=2 (Петр)
// Добавили в начало: Новый
// Стало: key=0 (Новый), key=1 (Иван), key=2 (Мария), key=3 (Петр)
// React перерисовывает ВСЕ элементы!
// ХОРОШО - key={id}
function UserList() {
const [users, setUsers] = useState([
{ id: 1, name: 'Иван' },
{ id: 2, name: 'Мария' },
{ id: 3, name: 'Петр' }
]);
const addUserAtStart = () => {
setUsers([{ id: 0, name: 'Новый' }, ...users]);
};
return (
<div>
<button onClick={addUserAtStart}>Добавить в начало</button>
{users.map((user) => (
<UserProfile key={user.id} user={user} />
))}
</div>
);
}
// Что происходит:
// Было: key=1 (Иван), key=2 (Мария), key=3 (Петр)
// Добавили в начало: Новый
// Стало: key=0 (Новый), key=1 (Иван), key=2 (Мария), key=3 (Петр)
// React добавляет ТОЛЬКО новый элемент!
Когда ключ особенно критичен
1. Фильтрация и сортировка
function TodoList() {
const [todos, setTodos] = useState([
{ id: 1, text: 'Купить молоко', done: false },
{ id: 2, text: 'Написать код', done: false },
{ id: 3, text: 'Поужинать', done: false }
]);
const [filter, setFilter] = useState('all');
const filtered = todos.filter((todo) => {
if (filter === 'done') return todo.done;
if (filter === 'pending') return !todo.done;
return true;
});
return (
<div>
<button onClick={() => setFilter('all')}>Все</button>
<button onClick={() => setFilter('done')}>Готовые</button>
<button onClick={() => setFilter('pending')}>Ожидают</button>
{filtered.map((todo) => (
// key={todo.id} ОБЯЗАТЕЛЕН!
// иначе state input смешается при фильтрации
<TodoItem key={todo.id} todo={todo} />
))}
</div>
);
}
2. Анимированные списки
function AnimatedList({ items }) {
return (
<ul>
{items.map((item) => (
// Без ключа анимация запустится на всех элементах!
<motion.li
key={item.id}
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
{item.name}
</motion.li>
))}
</ul>
);
}
3. Статефул компоненты в списке
function CounterList() {
const [items, setItems] = useState([
{ id: 1, count: 0 },
{ id: 2, count: 0 },
{ id: 3, count: 0 }
]);
function Counter({ item }) {
const [count, setCount] = useState(item.count);
return (
<div>
{item.id}: {count}
<button onClick={() => setCount(count + 1)}>+</button>
</div>
);
}
return (
<div>
{items.map((item) => (
// key={item.id} КРИТИЧЕН!
// Иначе state счетчиков будет смешиваться
<Counter key={item.id} item={item} />
))}
</div>
);
}
Как React использует ключи
// Без ключа - по индексу элемента
const elements = [
<div key="0">А</div>,
<div key="1">Б</div>,
<div key="2">В</div>
];
// Если изменить порядок или удалить:
const elements = [
<div key="0">Б</div>,
<div key="1">В</div>
];
// React видит key="0" и key="1", но содержимое изменилось!
// С правильными ключами:
const elements = [
<div key="A">А</div>,
<div key="B">Б</div>,
<div key="C">В</div>
];
// После переупорядочивания:
const elements = [
<div key="B">Б</div>,
<div key="C">В</div>
];
// React видит, что key="A" удален, и удаляет element A
// key="B" и key="C" переместились, но это ОН элементы
Правила выбора ключей
Хорошие ключи:
// 1. ID элемента (ЛУЧШЕ)
{items.map((item) => (
<div key={item.id}>{item.name}</div>
))}
// 2. UUID если нет ID
{items.map((item) => (
<div key={item.uuid}>{item.name}</div>
))}
// 3. Комбинированный ключ
{items.map((item) => (
<div key={`${item.userId}-${item.postId}`}>
{item.content}
</div>
))}
Плохие ключи:
// 1. НИКОГДА - index как ключ
{items.map((item, index) => (
<div key={index}>{item.name}</div> // ПЛОХО!
))}
// 2. НИКОГДА - Math.random()
{items.map((item) => (
<div key={Math.random()}>{item.name}</div> // УЖАСНО!
))}
// 3. Избегать - нестабильные вычисленные ключи
{items.map((item) => (
<div key={`item-${item.name}`}>
{item.name}
</div>
))} // Если name меняется, ключ меняется!
Практический пример: правильное использование
function App() {
const [todos, setTodos] = useState([
{ id: 1, title: 'Задача 1', completed: false },
{ id: 2, title: 'Задача 2', completed: false }
]);
const addTodo = () => {
const newTodo = {
id: Date.now(),
title: `Новая задача ${todos.length + 1}`,
completed: false
};
setTodos([...todos, newTodo]);
};
const deleteTodo = (id) => {
setTodos(todos.filter(t => t.id !== id));
};
const toggleTodo = (id) => {
setTodos(todos.map(t =>
t.id === id ? { ...t, completed: !t.completed } : t
));
};
return (
<div>
<button onClick={addTodo}>Добавить задачу</button>
<ul>
{todos.map((todo) => (
// ПРАВИЛЬНО: key={todo.id}
<li key={todo.id}>
<input
type="checkbox"
checked={todo.completed}
onChange={() => toggleTodo(todo.id)}
/>
{todo.title}
<button onClick={() => deleteTodo(todo.id)}>
Удалить
</button>
</li>
))}
</ul>
</div>
);
}
ESLint правило
// .eslintrc.js
{
rules: {
"react/jsx-key": "error" // Ошибка если ключей нет
}
}
Выводы
- Всегда используй ключи при рендере массивов
- Используй стабильные, уникальные значения (ID, UUID)
- Избегай index как ключа - это приводит к ошибкам
- Не используй Math.random() - каждый рендер новый ключ
- Ключ помогает React идентифицировать, какой элемент какой
- Сохраняет состояние - input values, toggles, счетчики
- Оптимизирует производительность - минимизирует перерисовку
Правильное использование ключей — это одна из основ эффективной разработки на React.