Как реализовать добавление и удаление кнопок с уникальными id в React при кликах
Что проверяют
Понимаете ли вы, откуда берутся стабильные ключи, как правильно обновлять массив в состоянии и где тут подвох с идентификаторами. Следом часто просят объяснить, почему нельзя взять индекс или useId.
Как отвечать
Состояние это массив объектов { id, label }, а не счётчик кнопок. Обновлять его нужно функционально, иначе быстрые клики и пакетная обработка обновлений прочитают устаревшее значение:
const nextId = useRef(1);
const add = () => {
const id = nextId.current++;
setButtons(prev => [...prev, { id, label: `Кнопка ${id}` }]);
};
const remove = id => setButtons(prev => prev.filter(b => b.id !== id));Способы получить уникальный id:
- счётчик в useRef: переживает перерисовки, растёт только вверх и не повторяется после удалений;
- crypto.randomUUID(): подходит, если id уходят на сервер или в хранилище, работает в защищённом контексте (HTTPS или localhost);
- библиотека nanoid: короткие случайные id, если нужна поддержка старых окружений.
Id создаётся в обработчике клика, а не во время рендера. В разметке key={b.id}, удаление через onClick={() => remove(b.id)}. У кнопки удаления стоит добавить aria-label с номером, иначе скринридер прочитает ряд одинаковых «Удалить».
Отдельно объясните, почему не годится useId: он выдаёт один стабильный id на экземпляр компонента для связки label и aria-*, а не генератор ключей для списка.
Где спотыкаются
Считают id как buttons.length + 1. Добавили три кнопки, удалили первую, следующая получит id 3 и совпадёт с существующей. Вариант «последний id плюс один» лучше, но после удаления последней кнопки id повторится.
Пишут onClick={remove(b.id)} без стрелочной функции. Удаление срабатывает прямо во время рендера, и компонент уходит в бесконечный цикл обновлений.
Ставят key={index} или key={Math.random()}. В первом случае после удаления состояние дочерних компонентов съезжает на соседей, во втором все элементы пересоздаются на каждой перерисовке.