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

相关代码
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检测不到状态变化,自然不会触发组件重绘。
解决步骤
替换类为普通对象类型
用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: '' } };如果需要类似类的逻辑(比如属性变更时触发操作),可以单独抽成工具函数,不要把状态和业务逻辑耦合在类里。
修正reducer中的状态更新逻辑
对于SetNewOrderState,不要用Object.assign(state.FormData, initialState),应该直接替换为初始的FormData:SetNewOrderState: (state) => { state.State = EditFormState.NewOrder; state.FormData = { ...initialState.FormData }; },确保组件正确订阅状态
组件中使用useSelector获取状态时,要确保返回的是直接依赖的属性,避免引用整个状态对象导致不必要的重绘,同时保证Redux能正确检测到状态变化。
内容的提问来源于stack exchange,提问作者DmitryB

