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 }));
});
}),
);
正如我所说,我不确定这是否是最佳实践,但在我的项目中我曾多次使用类似的东西。
我有 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 }));
});
}),
);
正如我所说,我不确定这是否是最佳实践,但在我的项目中我曾多次使用类似的东西。