You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React 18 Alpha项目Redux store出现非预期状态重复问题及解决

Redux状态重复生成问题排查与修复

项目依赖版本

React 18Alpha - Redux 7.2.4 - redux-thunk 2.3.0 - redux-storage 4.1.2

问题表现

Redux Store状态管理中,reducer会为每个节点重复生成状态,属于非预期表现:
问题图示

初始代码配置

根reducer定义

const rootReducer = combineReducers({
    serverSettings,
    userSettings,
});

初始userSettings reducer逻辑

export default function userSettingsReducer(
  state: IDefaultState = defaultState,
  action: UserSettingsAction
): IDefaultState {
  switch (action.type) {
    case USER_SETTINGS_SAVE:
      return { ...state, userSettings: action.userSettings };
    case USER_SETTINGS_LOAD:
      return { ...state, userSettings: action.userSettings };
    default:
      return state;
  }
}

action定义

export const ActionSaveUserSettings =
  (userSettings: UserSettings) => (dispatch: Dispatch<UserSettingsAction>) => {
    dispatch({ type: USER_SETTINGS_SAVE, userSettings: userSettings });
  };

action触发示例

const handleOrderSizesChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
        dispatch(ActionSaveUserSettings({ ...userSettings, OrderSizes: { ...userSettings.OrderSizes, [e.target.name]: e.target.value } }))
    }

store配置代码

import { createStore, combineReducers, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import { composeWithDevTools } from 'redux-devtools-extension';
import { createLogger } from 'redux-logger';
import serverSettings from "./reducers/serverSettingsReducer";
import userSettings from './reducers/userSettingsReducer';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
import { save, load } from "redux-localstorage-simple"

const rootReducer = combineReducers({
    serverSettings,
    userSettings,
});

const logger = createLogger();
const enhancer = composeWithDevTools(applyMiddleware(save(), thunk, logger));

export const store = createStore(rootReducer, load(), enhancer);
export type RootState = ReturnType<typeof store.getState>
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
export type AppDispatch = typeof store.dispatch
export const useAppDispatch = () => useDispatch<AppDispatch>()
export default store;

默认状态类型定义

export interface IDefaultState {
  serverSettings: ServerSettings;
  userSettings: UserSettings;
}

修复方案

调整userSettings reducer的参数和返回逻辑即可解决问题:

export default function userSettingsReducer(
  state: UserSettings = defaultState.userSettings,
  action: UserSettingsAction
): UserSettings {
  switch (action.type) {
    case USER_SETTINGS_SAVE:
      return { ...state, ...action.userSettings };
    case USER_SETTINGS_LOAD:
      return { ...state };
    default:
      return state;
  }
}

问题根因

combineReducers的作用是将多个子reducer合并,每个子reducer仅负责管理根状态中对应命名的切片:比如给combineReducers传入userSettings: userSettingsReducer,那么userSettingsReducer的入参state就只是根状态中userSettings字段的值,返回值也直接作为根状态userSettings字段的新值。
之前的错误写法有两个:

  1. 给userSettingsReducer的初始状态设置为完整的根状态IDefaultState,而非UserSettings类型的切片
  2. reducer返回值中额外嵌套了一层userSettings字段,导致最终状态出现store.userSettings.userSettings的重复嵌套结构
    修复后子reducer仅处理自己负责的状态切片,就解决了重复生成状态的问题。

内容的提问来源于stack exchange,提问作者Shane Bird

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 20:27:02