Использование индекса в качестве ключа — это анти-паттерн

Часто я вижу, что разработчики используют индекс (index) элемента в качестве его ключа (key), когда рендерят список.

{todos.map((todo, index) =>
<Todo {...todo}
key={index} />
)}

Это выглядит изящно и даже избавляет нас от сообщений в консоли (которые и были “реальной” проблемой, не так ли?). Где же кроется опасность?

Это возможно сломает Ваше приложение и отобразит неверные данные

Дайте мне объяснить, key — единственная вещь, которую React использует для идентификации DOM-элементов. Что произойдёт, если Вы поместите элемент в список или удалите что-то посередине? Если key точно такой же, что и прежде, то React предполагает, что DOM-элемент отображает тот же компонент, что и прежде. Но это уже не так.


Для демонстрации потенциальной опасности представляю простой пример (с исходниками).

Скриншот примера, показывающего опасность использования индекса в качестве ключа.

Оказывается, React будет использовать индекс (index), когда ничего не было передано в качестве ключа (key), потому что это лучшая возможность на данный момент. Кроме того, он предупредит Вас, что это неоптимальный вариант (но он говорит это немного запутанным языком, да). Если Вы предоставляете ключ самостоятельно, React думает, что Вы знаете, что делаете, но это —вспомните пример — может привести к непредсказуемым результатам.

Лучше

Каждый такой элемент должен иметь постоянное и уникальное свойство. В идеале, оно должно быть назначено при создании элемента. Конечно, я говорю об идентификаторе (id). Тогда мы сможем использовать его следующим образом:

{todos.map((todo) =>
<Todo {...todo}
key={todo.id} />
)}

Один из способов проделать всё это — просто переместить нумерацию на один шаг выше в абстракции. Использование глобального индекса гарантирует, что любые два элемента будут иметь разные идентификаторы.

todoCounter = 1;
function createNewTodo(text) {
return {
completed: false,
id: todoCounter++,
text
}
}

Намного лучше

Решение для продакшена должно использовать более надёжный подход, который будет в ответе за создание идентификаторов. Для этих случаев я рекомендую shortid. Он быстро генерирует “короткий непоследовательный дружественный к url уникальный” идентификатор. Код будет выглядеть примерно так:

var shortid = require('shortid');
function createNewTodo(text) {
return {
completed: false,
id: shortid.generate(),
text
}
}

TL; DR: сгенерировать уникальный идентификатор для каждого элемента и использовать его в качестве ключа при отображении списка.


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.