Redux中直接修改状态后调用action覆盖旧状态是否为不良实践?
结论
你的做法不符合Redux开发规范,属于典型的反模式。
核心问题分析
Redux最核心的设计原则就是状态不可变更新,禁止直接修改store中的任何状态对象:
- 你代码中直接对
getState()拿到的activeReport修改title属性的操作,已经在dispatch action之前就修改了store的原始状态,这是最核心的违规点。 - 你提到的"共享引用同步修改userReports"本质是直接修改原状态带来的副作用,这种隐式的状态变更完全脱离了Redux的状态管理流程,也没有任何可追溯性。
该写法的潜在风险
- 调试失效:Redux DevTools的时间旅行、变更历史记录功能都会完全失效,你无法追溯状态变更的触发时机和原因。
- 非预期逻辑执行:如果有其他selector或组件依赖
userReports,会在你dispatch action之前就接收到修改后的状态,可能触发意外的渲染或业务逻辑。 - 维护成本高:隐式的状态同步没有在reducer中体现,后续维护代码的开发者无法从reducer逻辑中看出
userReports会被修改,很容易写出隐形bug。
正确实现方案
你觉得手动同步两个状态繁琐的问题,完全可以通过标准的不可变更新逻辑解决,代码量非常少:
1. 修正thunk逻辑,不修改原状态
export const updateReport = () => async (dispatch, getState) => { try { const originalReport = getState().reports.activeReport // 生成新的报告对象,不修改原对象 const updatedReport = { ...originalReport, title: "blah blah blah" } dispatch({ type: ACTIONS.UPDATE_REPORT, payload: updatedReport }) } catch (error) { console.log(`ERROR: ${error.message}`) } }
2. 修正reducer逻辑,显式同步两个状态
case ACTIONS.UPDATE_REPORT: const updatedReport = action.payload return { ...state, activeReport: updatedReport, // 一行代码完成userReports同步,无需手动遍历处理 userReports: state.userReports.map(item => item.id === updatedReport.id ? updatedReport : item ) }
更简化的方案:使用Redux Toolkit
目前Redux官方已经全面推荐使用Redux Toolkit(RTK)进行开发,内置的Immer库允许你写"可变"语法,内部自动转为符合规范的不可变更新,代码更简洁:
import { createSlice } from '@reduxjs/toolkit' const initialState = { userReports: [], activeReport: null, } const reportsSlice = createSlice({ name: 'reports', initialState, reducers: { updateReport: (state, action) => { const updatedReport = action.payload state.activeReport = updatedReport const targetReport = state.userReports.find(item => item.id === updatedReport.id) if (targetReport) { // 直接修改即可,Immer会自动处理不可变更新 Object.assign(targetReport, updatedReport) } } } }) export const { updateReport } = reportsSlice.actions export default reportsSlice.reducer
内容的提问来源于stack exchange,提问作者Jo Momma
相关产品推荐
相关产品推荐

