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

使用Redux Toolkit createSlice时Reducer不生效,组件未重渲染问题

问题描述

我正在使用Redux Toolkit的快速入门文档,在TypeScript React应用中创建全局状态。在SetEditToPreviewState的reducer里,state参数本该是对象的Proxy,这样无需手动处理不可变状态就能修改属性。但修改state后组件树不重绘,调试器显示传入reducer的是类实例本身,而非Proxy包装器,调用栈也完全没用到Immer函数。

调试器中显示state为类实例而非Proxy

相关代码

file1.ts

export enum EditFormState {
  Edit,
  PreviewToPost,
  ShowResult,
  NewOrder
};

export class MyFormValues {
  private _softLicenTypeID: string = '';

  public set SoftLicenTypeID(value: string) {
    if (this._softLicenTypeID !== value) {
      this._softLicenTypeID = value;
      this.OnSoftLicenTypeIDChanged();
    }
  }

  public get SoftLicenTypeID(): string {
    return this._softLicenTypeID;
  }

  private OnSoftLicenTypeIDChanged() {}

  SoftLicenceTypeName: string = '';
  SoftLicenceTypeDescription: string = '';
  SoftLicencePriceComment: string = '';
}

export class GlobalStateType {
  public State: EditFormState = EditFormState.Edit;
  public FormData: MyFormValues = new MyFormValues();
}

const initialState: GlobalStateType = new GlobalStateType();

const mainStateSlice = createSlice({
  name: 'mainStateSlice',
  initialState,
  reducers: {
    //SetEditToPreviewState: (state: WritableDraft<GlobalStateType>, action: PayloadAction<MyFormValues>): void => {
    SetEditToPreviewState: (state, action: PayloadAction < MyFormValues > ) => {
      state.State = EditFormState.PreviewToPost;
      Object.assign(state.FormData, action.payload);
    },

    SetShowSentResultState: (state: WritableDraft < GlobalStateType > ): void => {
      state.State = EditFormState.ShowResult;
    },

    SetEditState: (state: WritableDraft < GlobalStateType > ): void => {
      state.State = EditFormState.Edit;
    },

    SetNewOrderState: (state: WritableDraft < GlobalStateType > ): void => {
      state.State = EditFormState.NewOrder;
      Object.assign(state.FormData, initialState);
    },
  },
});

export const mainStateSliceActions = mainStateSlice.actions;

file2.ts

function onSubmit(values: MyFormValues, actions: any) {
  //Object.assign(props.FormData, values);
  //actions.setSubmitting(false);
  //props.SetNextState(EditFormState.PreviewToPost);

  dispatch(mainStateSliceActions.SetEditToPreviewState(formData));
}

package.json

{
  "name": "eh-lib-dir-pay-react-app",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@reduxjs/toolkit": "^1.9.5",
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "@types/dateformat": "^5.0.0",
    "@types/jest": "^27.5.2",
    "@types/node": "^16.18.11",
    "@types/react": "^18.0.27",
    "@types/react-dom": "^18.0.10",
    "@types/validator": "^13.7.13",
    "axios": "^1.3.2",
    "bootstrap": "^5.2.3",
    "dateformat": "^5.0.3",
    "formik": "^2.2.9",
    "react": "^18.2.0",
    "react-bootstrap": "^2.7.0",
    "react-dom": "^18.2.0",
    "react-redux": "^8.0.5",
    "react-scripts": "5.0.1",
    "typescript": "^4.9.4",
    "validator": "^13.9.0",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "devDependencies": {
    "sass": "^1.62.0"
  }
}
原因分析及解决方案

核心原因

Redux Toolkit内置的Immer仅对普通JavaScript对象/数组生效,无法处理自定义类实例。你通过new GlobalStateType()和new MyFormValues()创建的是类实例,而非普通对象,Immer会跳过对这类对象的Proxy包装,直接传入原始实例。此时修改实例属性属于直接变更引用类型,Redux检测不到状态变化,自然不会触发组件重绘。

解决步骤

  1. 替换类为普通对象类型
    用TypeScript接口或类型别名定义状态结构,直接创建普通对象作为初始状态,不要用类。示例修改:

    export enum EditFormState {
      Edit,
      PreviewToPost,
      ShowResult,
      NewOrder
    };
    
    export interface MyFormValues {
      SoftLicenTypeID: string;
      SoftLicenceTypeName: string;
      SoftLicenceTypeDescription: string;
      SoftLicencePriceComment: string;
    }
    
    export interface GlobalStateType {
      State: EditFormState;
      FormData: MyFormValues;
    }
    
    const initialState: GlobalStateType = {
      State: EditFormState.Edit,
      FormData: {
        SoftLicenTypeID: '',
        SoftLicenceTypeName: '',
        SoftLicenceTypeDescription: '',
        SoftLicencePriceComment: ''
      }
    };
    

    如果需要类似类的逻辑(比如属性变更时触发操作),可以单独抽成工具函数,不要把状态和业务逻辑耦合在类里。

  2. 修正reducer中的状态更新逻辑
    对于SetNewOrderState,不要用Object.assign(state.FormData, initialState),应该直接替换为初始的FormData:

    SetNewOrderState: (state) => {
      state.State = EditFormState.NewOrder;
      state.FormData = { ...initialState.FormData };
    },
    
  3. 确保组件正确订阅状态
    组件中使用useSelector获取状态时,要确保返回的是直接依赖的属性,避免引用整个状态对象导致不必要的重绘,同时保证Redux能正确检测到状态变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:17:35