在React的reducer中如何更新数组内的指定对象?
实现正确性判断
现有实现是可用的
你提供的代码核心逻辑符合Redux的不可变更新规范,能够实现按id更新指定用户的需求:
- reducer中通过
map遍历用户数组,匹配到对应id的用户时合并新数据,其他用户直接返回,没有修改原数组,满足Redux状态不可变的要求 - thunk action中dispatch更新后同步将最新用户数组写入localStorage的逻辑也能正常执行
现有存在的可优化点
- 缺少参数校验:如果
action.payload没有携带id字段,或者updatedUser为非法值,会导致更新静默失败,没有任何提示 - 存在无用调试代码:action中的
console.log(updateUser)打印的是函数本身,没有实际意义,属于调试残留 - 持久化逻辑耦合:将localStorage写入逻辑硬编码在updateUser的action中,后续如果有其他操作也会修改用户列表,还需要重复编写持久化代码
- 大数据量下性能一般:当用户数组量级较大时,每次更新都全量遍历数组,性能会有损耗
优化方案
基础健壮性优化
首先添加参数校验,删除无用代码,优化后的代码示例如下:
优化后的reducer
case "UPDATE_USER_SUCCESS": // 开发环境下加非法参数提示 if (process.env.NODE_ENV === 'development' && !action.payload?.id) { console.error('更新用户的payload必须携带id字段') } // 参数非法直接返回原状态 if (!action.payload?.id) return state return { ...state, // 简化三元写法,逻辑和原代码一致 users: state.users.map(user => user.id === action.payload.id ? {...user, ...action.payload} : user ) };
优化后的action
export const updateUser = (updatedUser) => (dispatch, getState) => { // 提前校验参数合法性 if (!updatedUser || !updatedUser.id) { console.error('updateUser必须传入带id的用户对象') return } dispatch({ type: "UPDATE_USER_SUCCESS", payload: updatedUser, }); localStorage.setItem( "Users", JSON.stringify(getState().addUserReducer.users) ); };
解耦持久化逻辑
不要把持久化逻辑写在单个action里,可以全局监听store的用户状态变化自动同步到localStorage,只需要注册一次所有更新都会生效:
// 在store初始化文件中添加 store.subscribe(() => { const users = store.getState().addUserReducer.users localStorage.setItem('Users', JSON.stringify(users)) })
注册完成后所有action里的localStorage写入代码都可以删掉,避免重复代码。
大数据量级性能优化
如果用户数组量级超过1000条,可以把state中的用户存储结构从数组改成以id为键的对象,更新时间复杂度从O(n)降到O(1):
// 初始化state时把数组转成对象 const initialState = { users: JSON.parse(localStorage.getItem('Users') || '[]') .reduce((acc, user) => { acc[user.id] = user return acc }, {}) } // reducer更新写法 case "UPDATE_USER_SUCCESS": if (!action.payload?.id) return state return { ...state, users: { ...state.users, [action.payload.id]: { ...state.users[action.payload.id], ...action.payload } } } // 渲染列表时用Object.values(state.users)转成数组即可
推荐用Redux Toolkit简化代码
Redux官方现在推荐使用Redux Toolkit(RTK)开发,内置了immer支持不用手动写不可变更新的展开代码,也不需要手动定义action type,写法更简洁不易出错:
import { createSlice } from '@reduxjs/toolkit' const userSlice = createSlice({ name: 'addUser', initialState, reducers: { updateUserSuccess: (state, action) => { const targetUser = state.users.find(user => user.id === action.payload.id) if (targetUser) { // 直接修改即可,RTK内置immer会自动处理不可变更新 Object.assign(targetUser, action.payload) } } } }) // 自动生成对应的action export const { updateUserSuccess } = userSlice.actions export default userSlice.reducer
内容的提问来源于stack exchange,提问作者Anwar Hossain
相关产品推荐
相关产品推荐

