Почему нельзя использовать индекс массива как ключ в React
Что проверяют
Понимаете ли вы, зачем React нужен key, и видите ли разницу между «список перерисовался» и «состояние уехало не к тому элементу». Заодно проверяют, не заучен ли ответ: индекс запрещён не всегда.
Как отвечать
key это способ сопоставить элементы списка между двумя рендерами. React по нему решает, что элемент тот же самый и его DOM-узел с внутренним состоянием можно переиспользовать. Индекс такой идентичности не даёт: он описывает позицию, а не сущность.
Что ломается, если ключом взять индекс. Вставили элемент в начало списка, и у всех последующих индекс сдвинулся. Для React это выглядит так, будто изменились все элементы: он переиспользует старые узлы под новые данные. Данные при этом отрисуются верно, а вот всё, что живёт внутри узла, останется от прежнего элемента: введённый текст в поле, фокус, отметка чекбокса, локальный стейт дочернего компонента, анимация.
{todos.map((todo) => <TodoRow key={todo.id} todo={todo} />)}Ключ должен быть стабильным идентификатором самой сущности: id из базы, uuid, естественный ключ. Генерировать key={Math.random()} нельзя, это ещё хуже индекса: элемент будет пересоздаваться на каждом рендере.
Когда индекс допустим: список статичный, элементы не переупорядочиваются, ничего не вставляется и не удаляется в середину, внутри строк нет ни состояния, ни неконтролируемых полей ввода. Пример вроде вывода фиксированного набора подсказок.
Где спотыкаются
Считают, что проблема только в производительности. Производительность вторична, главная беда это перепутанное состояние и ввод пользователя.
Берут индекс, потому что «у данных нет id». Тогда идентификатор стоит завести на стороне загрузки данных, а не сочинять его при рендере.
Путают key с обычным пропом: прочитать props.key внутри компонента нельзя, это служебное поле React.