Redux Toolkit createSlice无法更新数组状态问题
问题现象
在Redux中存储对象数组,使用@reduxjs/toolkit的createSlice()编写状态更新逻辑时,仅state.push()新增元素的操作可以正常生效,其余修改、删除、整体替换数组的操作均无法触发状态更新。通过Redux DevTools观测,调用updateDashboard等方法后状态无任何变化,异步拉取的初始状态数组共包含10个元素。
问题复现代码如下:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'; import { BACKEND_API } from "../../utilities/environment"; import fetchViaApi from "../../utilities/fetchViaApi"; export const getInitialDashboards = createAsyncThunk( 'dashboard/getDashboards', async () => { const response = await fetchViaApi('/dashboards', { baseUrl: BACKEND_API, method: "GET" }); const data = await response.json(); return data; } ) const initialState = []; const dashboardsSlice = createSlice({ name: 'dashboards', initialState, reducers: { setDashboards: (state,action) => { state = action.payload; }, updateDashboard: (state,action) => { // state.push(action.payload); state = [...state.slice(0, 5)]; }, deleteDashboard: (state, action) => { }, }, extraReducers: builder => { builder.addCase(getInitialDashboards.fulfilled, (state, action) => { action.payload.forEach(element => { state.push(element); }); }) } }); export const { setDashboards, updateDashboard, editDashboard, deleteDashboard } = dashboardsSlice.actions; export default dashboardsSlice.reducer;
问题复现截图:
根本原因
createSlice 内部基于Immer实现不可变更新,reducer中的state是Immer包装的draft代理对象,状态更新只有两种合法写法:
- 直接修改draft对象本身(比如调用
push/splice等会改变原数组的方法、直接给数组下标位/对象属性赋值) - 显式
return全新的状态值,Immer会直接将该值作为新的state
代码中写的state = xxx只是给函数内的局部形参变量重新赋值,既没有修改原始draft对象,也没有返回新值,Immer完全感知不到状态变更,自然不会触发更新。这也是为什么state.push()可以生效——push是直接操作draft数组本身的变更,能被Immer捕获。
另外代码中导出action时多解构了未在reducers中定义的editDashboard,这个导出值会是undefined,调用时也会报错。
修复代码
按照Immer的规则调整reducer写法即可,参考实现如下:
const dashboardsSlice = createSlice({ name: 'dashboards', initialState, reducers: { setDashboards: (state,action) => { // 整体替换状态直接返回新值即可 return action.payload; }, updateDashboard: (state,action) => { // action.payload 约定为 { id: 待更新条目的ID, 其余为要更新的字段 } const updateData = action.payload; const targetIndex = state.findIndex(item => item.id === updateData.id); if (targetIndex > -1) { // 直接修改draft对应位置的对象,Immer会自动生成不可变更新 state[targetIndex] = { ...state[targetIndex], ...updateData }; } }, deleteDashboard: (state, action) => { // action.payload 约定为待删除条目的ID const deleteId = action.payload; const targetIndex = state.findIndex(item => item.id === deleteId); if (targetIndex > -1) { // 通过splice直接修改原draft数组完成删除 state.splice(targetIndex, 1); } }, }, extraReducers: builder => { builder.addCase(getInitialDashboards.fulfilled, (state, action) => { // 异步拉取初始值直接返回结果即可,不需要循环push return action.payload; }) } }); // 移除未定义的editDashboard导出 export const { setDashboards, updateDashboard, deleteDashboard } = dashboardsSlice.actions;
内容的提问来源于stack exchange,提问作者A. Hasemeyer
相关产品推荐
相关产品推荐

