Как работают редьюсеры в Redux
Что проверяют
Понимаете ли вы, почему редьюсер обязан быть чистой функцией и что сломается, если мутировать состояние. Следом почти всегда спрашивают про Redux Toolkit: почему в createSlice можно писать state.value++ и это не мутация.
Как отвечать
Редьюсер это функция (state, action) => newState. Store вызывает корневой редьюсер на каждый dispatch, результат становится новым состоянием, затем уведомляются подписчики. Требования:
- чистота: результат зависит только от аргументов. Никаких запросов, таймеров,
Math.random()иDate.now()внутри. Асинхронщина живёт в thunk-функциях (createAsyncThunk) или RTK Query, случайные значения готовят заранее; - неизменяемость: возвращается новый объект, старый не трогается.
useSelectorи мемоизированные селекторы сравнивают значения по ссылке, мутированное состояние выглядит прежним, и компонент не перерисуется. На этом же держится time-travel в Redux DevTools; - начальное состояние и чужие экшены: при
state === undefinedвернуть начальное, на незнакомыйtypeвернуть тот жеstate. Та же ссылка означает «ничего не поменялось».
combineReducers раздаёт каждому редьюсеру его ветку состояния, но каждый экшен получают все редьюсеры. Поэтому один экшен может обновить несколько срезов.
Руками редьюсеры сегодня пишут редко: официально рекомендуемый путь это Redux Toolkit. createSlice генерирует редьюсер и action creators, а внутри использует Immer: вы «мутируете» черновик, Immer строит новый объект и переиспользует нетронутые ветки. На экшены других срезов реагируют через extraReducers с builder-колбэком, объектная форма в RTK 2 удалена. configureStore в режиме разработки проверяет мутации и несериализуемые значения.
Где спотыкаются
В createSlice одновременно мутируют черновик и возвращают новый объект. Immer выбросит ошибку, нужно выбрать что-то одно.
Мутируют вложенные данные при ручном спреде: { ...state } копирует только верхний уровень, а state.items.push(x) меняет исходный массив, и подписчики изменения не заметят.
Кладут в состояние Date, промисы или экземпляры классов. Они ломают сериализацию, persist, DevTools и проверки configureStore.