NgRx全局store中如何区分不同组件实例的loader状态?
多实例Loader独立控制解决方案
核心思路
原有全局Store仅存储单个布尔类型的Loader状态,导致所有实例共享同一份状态。我们将Loader状态调整为按唯一ID索引的字典结构,每个Loader组件实例绑定唯一ID,仅响应对应ID的状态变更。
1. 调整Store相关逻辑
修正Loader状态类型定义
首先更新AppState中loader字段的类型,从boolean改为字典结构:
// app.state.ts export interface AppState { // 其他状态字段... loader: Record<string, boolean> }
调整Action定义
新增loaderId参数标识要操作的Loader实例:
// loader-state.actions.ts export const setLoader = createAction( '[Loader] Set loader', (props: { loaderId: string; loader: boolean }) => props ) // 可选:新增清理状态的Action,避免无用状态堆积 export const clearLoader = createAction( '[Loader] Clear loader', (props: { loaderId: string }) => props )
调整Reducer实现
初始状态改为空字典,按传入的loaderId更新对应状态:
// loader-state.reducer.ts const initialState: Readonly<Record<string, boolean>> = {} export const loaderReducer = createReducer( initialState, on(LoaderActions.setLoader, (state, { loaderId, loader }) => { return { ...state, [loaderId]: loader } }), on(LoaderActions.clearLoader, (state, { loaderId }) => { const newState = { ...state } delete newState[loaderId] return newState }) )
调整Selector为带参数版本
根据传入的loaderId返回对应实例的状态:
// loader-state.selector.ts export const selectLoaderById = createSelector( (state: AppState) => state.loader, (loaderState) => (loaderId: string) => loaderState[loaderId] ?? false )
2. 改造Loader组件
给组件添加ID输入属性,绑定对应ID的状态:
// loader.component.ts import { Input, OnDestroy } from '@angular/core' export class LoaderComponent implements OnInit, OnDestroy { @Input() loaderId!: string loader$: Observable<boolean> constructor(private store: Store<AppState>) {} ngOnInit(): void { this.loader$ = this.store.pipe( select(selectLoaderById), map(selectorFn => selectorFn(this.loaderId)) ) } ngOnDestroy(): void { // 组件销毁时清理对应状态,可选 this.store.dispatch(clearLoader({ loaderId: this.loaderId })) } }
模板无需修改,保持原有逻辑即可:
<!-- loader.component.html --> <p [hidden]='!(loader$ | async)'>loader!</p>
3. 业务侧使用
给每个Loader实例传入唯一ID,触发状态变更时携带对应ID即可实现独立控制:
<!-- 用户列表组件 --> <app-loader loaderId="user-list-loader"></app-loader> <!-- 表单提交组件 --> <app-loader loaderId="form-submit-loader"></app-loader>
触发对应Loader显隐:
// 打开用户列表Loader this.store.dispatch(setLoader({ loaderId: 'user-list-loader', loader: true })) // 关闭用户列表Loader this.store.dispatch(setLoader({ loaderId: 'user-list-loader', loader: false }))
轻量替代方案
如果你的Loader仅需要在所属父组件内部控制,不需要跨组件/路由触发,无需改造全局Store,直接给Loader组件添加公共方法,父组件通过@ViewChild调用控制即可,实现更简单。
内容的提问来源于stack exchange,提问作者Peter Boomsma
相关产品推荐
相关产品推荐

