Angular 17中Ngrx Selectors返回相同数据集问题求助
问题原因
1. 同一份Reducer被用于两个Feature,且Action无Feature区分
你为selectedPhotos和XboxPhotos两个Feature都注册了同一个photoReducer,而这个Reducer会响应全局的photosFetchAPISuccess Action。当你dispatch这个Action时,所有使用该Reducer的Feature都会执行状态更新,导致两个Feature的状态被同时覆盖。
2. 组件中错误注入多个Store实例
NgRx的Store是单例服务,不需要为不同Feature注入多个Store实例。你注入的store、filterStore、xboxStore其实都是同一个全局Store,这样的写法没有意义,反而容易混淆。
修复步骤
步骤1:为每个Feature创建独立的Action和Reducer
需要给两个Feature分别定义专属的Action和Reducer,确保状态更新相互隔离:
针对selectedPhotos的Action:
// selected-photos.actions.ts import { createAction, props } from '@ngrx/store'; import { ProductResponse, filterValues } from './your-types'; export const invokeSelectedPhotosAPI = createAction( '[Selected Photos API] Invoke Photo Fetch API', props<{ filter: filterValues }>() ); export const selectedPhotosFetchAPISuccess = createAction( '[Selected Photos API] Fetch Success', props<{ results: ProductResponse['results'], item_Count: ProductResponse['item_Count'] }>() );
针对XboxPhotos的Action:
// xbox-photos.actions.ts import { createAction, props } from '@ngrx/store'; import { ProductResponse, filterValues } from './your-types'; export const invokeXboxPhotosAPI = createAction( '[Xbox Photos API] Invoke Photo Fetch API', props<{ filter: filterValues }>() ); export const xboxPhotosFetchAPISuccess = createAction( '[Xbox Photos API] Fetch Success', props<{ results: ProductResponse['results'], item_Count: ProductResponse['item_Count'] }>() );
针对selectedPhotos的Reducer:
// selected-photos.reducer.ts import { createReducer, on } from '@ngrx/store'; import { ProductResponse } from './your-types'; import { selectedPhotosFetchAPISuccess } from './selected-photos.actions'; export const initialSelectedPhotoState: ProductResponse = { results: [], item_Count: 0 }; export const selectedPhotosReducer = createReducer( initialSelectedPhotoState, on(selectedPhotosFetchAPISuccess, (state, { results, item_Count }) => { return { ...state, results, item_Count }; }) );
针对XboxPhotos的Reducer:
// xbox-photos.reducer.ts import { createReducer, on } from '@ngrx/store'; import { ProductResponse } from './your-types'; import { xboxPhotosFetchAPISuccess } from './xbox-photos.actions'; export const initialXboxPhotoState: ProductResponse = { results: [], item_Count: 0 }; export const xboxPhotosReducer = createReducer( initialXboxPhotoState, on(xboxPhotosFetchAPISuccess, (state, { results, item_Count }) => { return { ...state, results, item_Count }; }) );
步骤2:更新AppModule中的Feature注册
使用各自独立的Reducer:
// app.module.ts import { StoreModule } from '@ngrx/store'; import { selectedPhotosReducer } from './selected-photos.reducer'; import { xboxPhotosReducer } from './xbox-photos.reducer'; @NgModule({ imports: [ // ...其他模块 StoreModule.forFeature('selectedPhotos', selectedPhotosReducer), StoreModule.forFeature('XboxPhotos', xboxPhotosReducer), ] }) export class AppModule { }
步骤3:更新Effect,分别处理两个Action
修改Effect,监听两个不同的Action,分别触发对应的成功Action:
// photos.effects.ts import { Actions, createEffect, ofType } from '@ngrx/effects'; import { mergeMap, map } from 'rxjs/operators'; import { PhotosService } from './photos.service'; import { invokeSelectedPhotosAPI, selectedPhotosFetchAPISuccess } from './selected-photos.actions'; import { invokeXboxPhotosAPI, xboxPhotosFetchAPISuccess } from './xbox-photos.actions'; @Injectable() export class PhotosEffects { loadSelectedPhotos$ = createEffect(() => this.actions$.pipe( ofType(invokeSelectedPhotosAPI), mergeMap((prod) => this.photosService.getPhotos(prod.filter).pipe( map((data) => selectedPhotosFetchAPISuccess({ results: data.results, item_Count: data.item_Count })) ) ) ) ); loadXboxPhotos$ = createEffect(() => this.actions$.pipe( ofType(invokeXboxPhotosAPI), mergeMap((prod) => this.photosService.getPhotos(prod.filter).pipe( map((data) => xboxPhotosFetchAPISuccess({ results: data.results, item_Count: data.item_Count })) ) ) ) ); constructor(private actions$: Actions, private photosService: PhotosService) {} }
步骤4:更新Selector(保持原有逻辑即可)
// photos.selectors.ts import { createFeatureSelector, createSelector } from '@ngrx/store'; import { ProductResponse } from './your-types'; export const getPhotoState = createFeatureSelector<ProductResponse>('selectedPhotos'); export const selectPhotos = createSelector(getPhotoState, (state) => state.results); export const getXboxPhotoState = createFeatureSelector<ProductResponse>('XboxPhotos'); export const XboxPhotos = createSelector(getXboxPhotoState, (state) => state.results);
步骤5:修改组件中的Store注入和dispatch逻辑
只需要注入一个Store实例,然后dispatch对应的Action:
// your-component.component.ts import { Store } from '@ngrx/store'; import { invokeSelectedPhotosAPI } from './selected-photos.actions'; import { invokeXboxPhotosAPI } from './xbox-photos.actions'; import { selectPhotos, XboxPhotos } from './photos.selectors'; @Component({ /* ... */ }) export class YourComponent { photos$ = this.store.pipe(select(selectPhotos)); xboxPlatform$ = this.store.pipe(select(XboxPhotos)); constructor(private store: Store) {} ngOnInit() { // 获取全量数据 this.store.dispatch(invokeSelectedPhotosAPI({ filter: this.filterValues })); // 获取Xbox过滤数据 const xBoxFilter = { /* 你的过滤条件 */ }; this.store.dispatch(invokeXboxPhotosAPI({ filter: xBoxFilter })); } }
核心逻辑说明
通过为每个Feature创建专属的Action和Reducer,确保当某个Action被dispatch时,只有对应的Reducer会响应并更新自身Feature的状态,从而实现两个Feature Store的完全隔离。同时,NgRx的Store是单例,不需要注入多个实例,所有Feature的状态都在同一个Store的不同切片中。
内容的提问来源于stack exchange,提问作者bilpor
相关产品推荐
相关产品推荐

