You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 02:36:04