ngrx 从效果中更新状态

ngrx updating state from effects

我有 ngrx 存储设置,用于存储当前活动的用户和用户的区域设置。根据用户区域设置,我的拦截器将在所有 API 调用中注入 X-LOCALE header。当调用 select 用户的操作时,我首先调用单独的 API 来获取该用户的区域设置,将该区域设置传递给拦截器,然后继续调用其余的 APIs(此时 X-LOCALE header 应该注入)。问题:如何从 user-effects.ts 触发状态变化?

user-reducer.ts

export interface UsersState extends EntityState<UserInfo> {
  loading: boolean;
  currentUser: UserInfo;
  locale: string;
}
const usersReducer = createReducer(
  INITIAL_STATE,
  on(actions.fetchUser, 
  ...bunch of other actions...,
  (state) => ({ ...state, loading: true })
);

user-actions.ts

...bunch of actions
export const fetchUser = createAction('[USERS] Fetch User Request', props<{ uuid: string }>());

user-effects.ts

@Injectable()
export class UsersEffects {
  fetchUser$ = createEffect(() =>
    this.actions$.pipe(
      ofType(usersActions.fetchUser),
      switchMap(({ uuid }) => {
        // here we would read user locale
        return this.usersService.fetchUserLocale(uuid).pipe(
          map((locale) => {
            // ******* here i need to update locale in the state ********
            return uuid;
          });
        }),
      );
    }),
    switchMap((uuid) => {
      // all calls from here on should have "X-LOCALE" header injected
      return forkJoin([
        this.usersService.fetchUser(uuid),
        this.usersService.fetchUserCards(uuid),
        this.usersService.fetchUserPermissions(uuid),
      ]).pipe(
        map(([userDto, cards, permissions]) => {
          const userInfo = { ...userDto, cards, permissions };
          return usersActions.fetchUserSuccess({ userInfo });
        }),
        catchError((error) => of(usersActions.fetchUserFailure({ error }))),
      );
    }),
  ),
);

这里 locale.interceptor.ts 会注入 headers

@Injectable()
  export class HttpHeadersInterceptor implements HttpInterceptor {
    constructor(private store$: Store<UsersState>) {
      this.store$.pipe(select((state) => state.locale)).subscribe((locale: string) => {
        this.locale = locale;
      });
    }
    private locale: string;

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
      const defaultHeaders = { Accept: 'application/json, text/plain' };

      if (this.locale) {
        defaultHeaders['X-LOCALE'] = this.locale;
      }

      const authReq = req.clone({ setHeaders: defaultHeaders });

      return next.handle(authReq);
    }
  }

嗯,我不确定这是否是最佳做法,但您仍然可以从 switchMap 中发送操作,可以直接从 map 中发送,也可以将其更改为 tap 因为没有实际的映射,所以它只是一个副作用。像这样。

  switchMap(({ uuid }) => {
    // here we would read user locale
    return this.usersService.fetchUserLocale(uuid).pipe(
      tap((locale) => {
        this.store.dispatch(desiredAction({ uuid }));
      });
    }),
  );

正如我所说,我不确定这是否是最佳实践,但在我的项目中我曾多次使用类似的东西。