Ngrx技术问题:如何仅监听数组中单个对象的变化
搞定NgRx监听选中用户时的不必要触发问题
你遇到的问题其实是NgRx状态不可变性和选择器设计导致的,我给你拆解下原因和解决方案:
问题根源
- 你用
selectAll监听整个用户数组,只要数组里任何一个用户有更新,这个选择器就会发射全新的数组引用——毕竟NgRx状态是不可变的,任何修改都会生成新的状态对象。 - 哪怕选中的用户根本没变化,每次
find返回的对象可能因为数组是新的,被Observable判定为“新值”,导致订阅触发。
最优解决方案:用NgRx Entity的专属选择器
NgRx Entity已经帮我们封装了根据ID获取单个实体的工具,直接用它就能精准监听选中用户的变化,完全避免监听整个数组:
第一步:在Reducer里添加选中用户的选择器
修改你的Reducer代码,导出Entity Adapter的选择器,并创建专门获取选中用户的选择器:
export const userAdapter = createEntityAdapter<UserModel>({ selectId: x => x.UserId }); export interface UserlistState extends EntityState<UserModel>{ selectedUserId: number }; export const initialState: UserlistState = userAdapter.getInitialState({ selectedUserId: undefined, }); // 导出Entity自带的选择器 export const { selectEntities, selectById } = userAdapter.getSelectors(); // 先保留你原来的获取选中ID的选择器 const getSelectedUserIdState = (state) => state.userlist.selectedUserId; export const getSelectedUserId = createSelector(getSelectedUserIdState, userId => userId); // 新增:直接获取选中用户的选择器 export const getSelectedUser = createSelector( selectEntities, getSelectedUserId, (entities, selectedId) => selectedId ? entities[selectedId] : undefined );
第二步:修改订阅代码
现在直接监听这个新的选择器,只有当选中ID变化,或者选中的用户本身更新时,才会触发订阅:
watchSelectedUser = (): Observable<UserModel> => { return this.userlistStore.select(userlistReducers.getSelectedUser); };
临时替代方案:用distinctUntilChanged过滤重复值
如果暂时不想改动Reducer,可以在订阅的pipe里加上distinctUntilChanged,手动判断用户对象是否真的变化了:
watchSelectedUser = (): Observable<UserModel> => { return this.userlistStore.select(userlistReducers.selectAll).pipe( map(users => users.find(x => x.UserId == this.getSelectedUserId())), // 自定义比较逻辑,只在用户核心属性变化时触发 distinctUntilChanged((prevUser, currUser) => { if (!prevUser && !currUser) return true; if (!prevUser || !currUser) return false; // 这里列出你关心的用户属性,比如UserId、Name、Email等 return prevUser.UserId === currUser.UserId && prevUser.Name === currUser.Name && prevUser.Email === currUser.Email; }) ); };
不过这个方法只是“治标”,因为每次数组变化还是会执行find操作,只是过滤掉了无意义的重复触发,不如第一种方案高效。
总结
第一种方案利用NgRx Entity的特性,直接精准监听目标实体,是最符合NgRx设计理念的做法,能从根源上解决不必要的订阅触发问题。
内容的提问来源于stack exchange,提问作者Shyamal Parikh
相关产品推荐
相关产品推荐

