Какие проблемы возникают при дублировании состояния и почему его нужно избегать?
Что проверяют
Умеете ли вы проектировать состояние так, чтобы у каждого значения был один источник. Это прямая проверка опыта с React: дублирование рождает баги вида «поменял тут, а там осталось старое». Следом часто просят найти лишнее состояние в куске кода.
Как отвечать
Покажите типичные формы дублирования и замену для каждой:
- производное состояние:
itemsиfilteredItemsв двухuseState. Отфильтрованный список вычисляется при рендере изitemsиquery, при дорогом вычислении черезuseMemo; - копия пропа в состоянии:
useState(props.value)берёт значение только при первом монтировании, дальше проп меняется, а состояние нет. Либо компонент управляемый, либо сброс черезkey; - копия объекта вместо ссылки: храните
selectedId, а не сам выбранный объект. Иначе после редактирования элемента в списке выбранным остаётся старая версия; - серверные данные в двух местах: ответ API лежит и в кэше TanStack Query, и в Redux-сторе. Кэш запросов уже и есть источник, копировать его в стор не нужно;
- состояние, которое уже живёт в URL: фильтры и номер страницы в адресе и параллельно в сторе расходятся при навигации назад.
Почему это плохо: копии рано или поздно рассинхронизируются, а синхронизация через useEffect с setState даёт лишний рендер с устаревшим промежуточным значением и цепочки эффектов, которые трудно отлаживать. Если значение нужно двум компонентам, его поднимают к общему родителю, для глобального состояния берут стор (Redux Toolkit, Zustand) или контекст. Сошлитесь на правило из документации React: избегайте избыточного и дублирующегося состояния.
Где спотыкаются
Называют решением только Redux. Большая часть дублирования убирается вычислением при рендере, без всякой библиотеки.
Синхронизируют копии через useEffect и считают это нормой. Эффект нужен для внешних систем, а не для пересчёта одних данных в другие.
Путают дублирование с мемоизацией. useMemo над данными из одного источника не создаёт второе состояние, а вот вычисленный список, положенный в useState, уже создаёт.