JavaScript Redux Reducer中如何合并find与map实现数组条件更新或新增
Redux Reducer 中合并 find/map 操作的不可变实现
原代码存在的典型问题
- 直接调用
userRoleArray.push()属于对原数组的就地修改,违反Redux要求的状态不可变性,会导致数组引用不发生变化,组件浅比较时无法感知状态更新,触发不了重渲染 - 先单独执行
find做存在性判断、再执行map做匹配更新,相当于对数组做了两次全量遍历,存在不必要的性能开销
合并逻辑的核心思路
不需要单独前置调用find方法,可以通过单次数组遍历同步完成匹配判断、记录更新/新增的全流程,全程生成全新的数组、对象引用,完全符合Reducer的纯函数要求:
- 遍历数组生成新副本的过程中,逐个校验当前元素的
userId是否与目标值匹配 - 如果匹配,就返回追加了新角色的全新用户对象,同时标记「数组中已存在对应用户记录」
- 整轮遍历结束后,如果标记显示没有匹配到目标用户,就把完整的新用户记录拼接到新数组的末尾
可直接使用的实现代码
/** * 更新用户角色数组 * @param {Array} userRoleArray 当前的用户角色状态数组 * @param {Number} targetUserId 目标用户ID * @param {Object} newUserRole 待追加的新角色对象 * @returns {Array} 更新后的全新数组 */ const updateUserRole = (userRoleArray, targetUserId, newUserRole) => { let hasMatched = false // map会返回全新数组,和原数组无引用关联 const nextArray = userRoleArray.map(item => { if (item.userId === targetUserId) { hasMatched = true // 浅拷贝用户对象、拷贝角色数组,保证嵌套数据的引用也发生变化 return { ...item, userRoles: [...item.userRoles, { ...newUserRole }] } } return item }) // 未找到匹配用户时,在新数组上追加完整记录(不会影响原状态数组) if (!hasMatched) { nextArray.push({ userId: targetUserId, userRoles: [newUserRole] }) } return nextArray }
场景验证
- 传入
userId=2、userRoles={ userRoleId:4, roleName:"D" }时,遍历到userId=2的记录会触发匹配标记,直接在该用户的原有角色列表后追加新的D角色,返回全新的数组引用 - 传入
userId=4、userRoles={ userRoleId:4, roleName:"D" }时,遍历完全部现有记录都不会触发匹配标记,最终会把包含完整用户信息的新记录追加到数组末尾
额外说明
这种写法完全规避了原代码的就地修改问题,同时把原来两次数组遍历压缩为一次,既满足Redux的不可变要求,也减少了冗余操作。
内容的提问来源于stack exchange,提问作者Moon
相关产品推荐
相关产品推荐

