Списки и ключи (key)
Превращаем массивы данных в списки элементов и разбираемся, зачем React требует уникальный key.
key — специальный проп, по которому React отличает элементы списка между перерисовками, чтобы обновлять DOM точечно.
Рендер массива через map
Чтобы показать список, данные превращают в массив JSX-элементов методом map. React умеет рендерить массив элементов:
function TodoList() {
const todos = ["Купить хлеб", "Позвонить маме", "Сделать React"];
return (
<ul>
{todos.map((todo) => (
<li>{todo}</li>
))}
</ul>
);
}
Код пока неполон — React выдаст предупреждение «Each child in a list should have a unique key prop».
Зачем нужен key
Когда список меняется (добавили, удалили, переставили элемент), React сравнивает старую и новую версии. Без подсказки ему трудно понять, какой элемент какому соответствует. key — это «удостоверение личности» элемента: по нему React точно знает, что обновить, что оставить, а что удалить.
function TodoList({ todos }) {
return (
<ul>
{todos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
Ключ должен быть уникальным среди соседей и стабильным — обычно это id из данных.
Почему индекс — плохой ключ
Соблазн использовать индекс (map((item, i) => ... key={i})) велик, но это источник коварных багов. Индекс привязан к позиции, а не к элементу. Если вставить элемент в начало списка, все индексы сдвинутся, и React решит, что поменялись все элементы.
Покажем на JS, почему индекс «съезжает» при вставке в начало:
let items = [{ id: 7, text: "B" }, { id: 9, text: "C" }];
items.forEach((it, i) => console.log("индекс", i, "→ id", it.id));
console.log("--- вставили новый элемент в начало ---");
items = [{ id: 5, text: "A" }, ...items];
items.forEach((it, i) => console.log("индекс", i, "→ id", it.id));
Вывод:
индекс 0 → id 7 индекс 1 → id 9 --- вставили новый элемент в начало --- индекс 0 → id 5 индекс 1 → id 7 индекс 2 → id 9
Видно: элемент с id 7 был под индексом 0, а стал под индексом 1. Если ключом был индекс, React «перепутает» элементы — например, текст в поле ввода окажется не у того элемента. С ключом по id такого не случится: id привязан к самому элементу.
Когда индекс допустим
Только если список статичен: не меняется порядок, не добавляются и не удаляются элементы. В остальных случаях — стабильный id.
Где ставить key
Ключ ставится на самый внешний элемент внутри map, а не где-то внутри:
{users.map((u) => (
<div key={u.id} className="card">
<h3>{u.name}</h3>
</div>
))}
Итог
- Списки рендерят через
map, превращая данные в массив JSX-элементов. - Каждому элементу нужен
key— уникальный и стабильный (обычноid). - Индекс как ключ ломает списки при вставке/удалении/перестановке;
keyставят на внешний элемент.