Стабильные ключи и списки
Урок про key в списках: почему стабильные ключи критичны для корректности и производительности, и почему индекс — плохой ключ.
key — это «удостоверение личности» элемента списка. По ключу React понимает, какой элемент сохранить, какой добавить, а какой удалить между рендерами.
Зачем нужны ключи
Когда вы рендерите список через map, React при следующем рендере должен сопоставить старые элементы с новыми. Без подсказки он сравнивал бы по позиции — и при вставке в начало списка считал бы изменившимися все элементы. key даёт каждому элементу устойчивую идентичность: React переносит существующий DOM-узел и состояние к тому же ключу, а не пересоздаёт всё подряд.
{users.map((user) => (
<UserCard key={user.id} user={user} />
))}
Почему индекс массива — плохой ключ
Соблазнительно написать key={index}. Это работает, пока список не меняет порядок и не вставляет/удаляет в середине. Иначе индексы «съезжают»: элемент с key=0 теперь указывает на другой объект данных. React решит, что это тот же элемент, и переиспользует его DOM и внутреннее состояние — что приводит к классическому багу: введённый в поле текст «прилипает» не к той строке после удаления.
| Ключ | Когда безопасен |
user.id (стабильный из данных) | всегда — лучший выбор |
| индекс массива | только если список статичен: не сортируется, не фильтруется, не меняет длину в середине |
Math.random() | никогда: новый ключ каждый рендер ⇒ полное пересоздание списка |
Демонстрация «сдвига» в чистом JS
Покажем на запускаемом примере, как индекс перестаёт соответствовать элементу после вставки в начало. Ключи по id остаются привязаны к данным, а индексы — нет.
let list = [
{ id: "a", text: "Алиса" },
{ id: "b", text: "Борис" },
];
// что "видит" React, если ключ = индекс
console.log("До вставки (key=index):");
list.forEach((item, i) => console.log(" key", i, "=>", item.text));
// вставляем нового пользователя В НАЧАЛО
list = [{ id: "z", text: "Зоя" }, ...list];
console.log("После вставки (key=index):");
list.forEach((item, i) => console.log(" key", i, "=>", item.text));
console.log("Вывод: key=0 теперь Зоя, хотя раньше был Алиса — React переиспользует не тот узел");
Вывод:
До вставки (key=index): key 0 => Алиса key 1 => Борис После вставки (key=index): key 0 => Зоя key 1 => Алиса key 2 => Борис Вывод: key=0 теперь Зоя, хотя раньше был Алиса — React переиспользует не тот узел
Правила хороших ключей
- Ключ должен быть стабильным (один и тот же элемент данных — всегда один ключ) и уникальным среди братьев.
- Берите ключ из данных:
idиз БД, slug, естественный уникальный признак. - Нет
id? Сгенерируйте стабильный идентификатор один раз при создании элемента (например,crypto.randomUUID()при добавлении), а не на каждый рендер. - Ключ нужен только на верхнем элементе, возвращаемом из
map, и только среди соседей — глобальной уникальности не требуется.
Итог
keyдаёт элементу устойчивую идентичность между рендерами.- Индекс как ключ ломает порядок и состояние при вставках/сортировке/удалении.
- Лучший ключ — стабильный
idиз данных;Math.random()— никогда.