未触发对应Action时Redux Store变更关联组件Props问题排查
我来帮你拆解这个奇怪的问题——核心原因其实是JavaScript引用类型的浅拷贝陷阱,咱们一步步理清楚:
问题根源
你提到的现象:仅更新userRolesForUsersRequestSuccess reducer,但initialUserRolesForUsersRequestSuccess的数据也跟着变,结合你补充的“传入不同数据就不会同步”的排查结果,基本可以确定:
两个 reducer 存储的userRoles数组/对象,指向了同一个内存引用。
具体来说,当初始化initialUserRolesForUsersRequestSuccess时,你传入的后端数据,和后续更新userRolesForUsersRequestSuccess时传入的数据,是同一个对象/数组的引用。因为JavaScript中引用类型(对象、数组)的赋值是传引用而非传值,所以修改其中一个引用的内容,另一个也会跟着变化——哪怕它们存在不同的reducer里。
具体代码问题点
看你的reducer实现,都是直接返回action的payload:
// 两个reducer都是直接返回action传入的对象 return action.userRoleDataForUsers; return action.initialUserRoleData;
如果这两个action传入的payload是同一个引用(比如后端返回数据后,直接把同一个对象同时传给了两个action),那么两个reducer的state就会指向同一个内存地址,后续修改任意一个都会影响另一个。
解决方案
我们需要确保两个reducer的state是独立的引用,下面是几种可行的方案:
方案1:在分发Action时做深拷贝
在调用action创建函数前,对后端返回的数据做深拷贝,确保两个action的payload是完全独立的:
// 假设backendResponse是后端返回的原始数据 // 初始化初始角色时,深拷贝一份数据 const initialRoleData = JSON.parse(JSON.stringify(backendResponse)); dispatch(setInitialUserRolesForUsersRequestSuccess(initialRoleData)); // 后续UI修改角色时,基于当前数据再做拷贝,避免影响初始数据 const updatedRoleData = JSON.parse(JSON.stringify(currentUserRoles)); // 对updatedRoleData做修改操作 dispatch(setUserRolesForUsersRequestSuccess(updatedRoleData));
注:
JSON.parse(JSON.stringify())适合结构简单的数据,如果你的数据包含函数、Symbol等特殊类型,建议使用lodash.cloneDeep这类专业的深拷贝工具。
方案2:在Reducer中返回新的引用
修改reducer,确保每次返回的state都是新的对象/数组,而不是直接返回action的payload:
export function userRolesForUsersRequestSuccess(state = {userRoles: []}, action) { switch(action.type) { case 'USER_ROLES_FOR_USERS_REQUEST_SUCCESS': // 返回新对象,同时对数组也做浅拷贝(如果是数组的话) return { ...action.userRoleDataForUsers, userRoles: [...action.userRoleDataForUsers.userRoles] }; default: return state; } } export function initialUserRolesForUsersRequestSuccess(state = {userRoles: []}, action) { switch (action.type) { case 'INITIAL_USER_ROLES_FOR_USERS_REQUEST_SUCCESS': return { ...action.initialUserRoleData, userRoles: [...action.initialUserRoleData.userRoles] }; default: return state; } }
这样哪怕action传入的是同一个引用,reducer也会返回新的对象和数组,切断两个state之间的引用关联。
方案3:检查数据初始化逻辑
确保在后端返回数据后,给两个action传递的是不同的引用。比如:
// 后端返回数据后,先拷贝一份给初始角色,原数据用于UI修改 const backendData = await fetchUserRoles(); dispatch(setInitialUserRolesForUsersRequestSuccess([...backendData.userRoles])); // 原数据可以直接用于UI修改的reducer(如果后续会修改的话,最好还是拷贝) dispatch(setUserRolesForUsersRequestSuccess([...backendData.userRoles]));
验证思路
你可以在Redux DevTools里查看两个reducer的state的引用地址(或者在代码里用Object.is()对比两个state),如果返回true,就说明是同一个引用,这时候用上面的方案修复即可。
内容的提问来源于stack exchange,提问作者Mirko Flyktman

