Что произойдет при прямой мутации состояния в редьюсере Redux
Что проверяют
Понимаете ли вы, что подписка сравнивает ссылки, а не содержимое, и знаете ли, где мутация разрешена, а где ломает приложение. Ответ «так принято, состояние должно быть неизменяемым» закрывает вопрос наполовину.
Как отвечать
Стор решает, изменилось ли состояние, сравнивая ссылку на старый и новый объект. Код вида state.items.push(item); return state возвращает тот же объект, и с точки зрения стора не произошло ничего. Последствия:
useSelectorиconnectне перерисуют компонент, экран отстаёт от данных и догоняет их скачком на следующем несвязанном действии;- мемоизированные селекторы и
React.memoтоже сравнивают по ссылке и отдают устаревший результат; - отладчик перестаёт работать: перемещение по истории держится на том, что прошлые состояния никто не трогал;
- редьюсер перестаёт быть чистой функцией, и одна и та же последовательность действий больше не даёт одинаковый результат.
Правильных путей два. Возвращать новый объект со сборкой через расширение ({ ...state, count: state.count + 1 }) или писать редьюсер в createSlice, где Immer подсовывает черновик: там state.items.push(item) законная запись, из черновика собирается новый неизменяемый объект. Важно добавить, что это работает только внутри редьюсера слайса, и что в режиме разработки Redux Toolkit включает проверки, которые ругаются на мутацию состояния между действиями.
Где спотыкаются
Копируют верхний уровень и меняют вложенный объект. Расширение копирует один уровень, поэтому правка полей внутри state.user остаётся мутацией, просто спрятанной глубже, и симптом появляется только на вложенных экранах.
Говорят, что мутация приводит к лишним перерисовкам. Всё наоборот: перерисовки пропадают, потому что ссылка не поменялась. Из-за неверного диагноза баг ищут в производительности, хотя выглядеть он должен как «в сторе всё правильно, а компонент не обновился».
Переносят привычку из createSlice наружу: сортируют массив из стора прямо в компоненте или правят объект из полезной нагрузки действия. sort, reverse, splice меняют исходный массив, поэтому нужна копия или неизменяющие варианты вроде toSorted, иначе состояние тихо портится мимо редьюсера.