我有一个检查网格是否已加载的函数,如果没有,它将触发加载。但是目前,这最终会针对相同的Loaded
值触发多次,因此它将多次调用相关动作。我的印象是,存储选择器默认情况下仅发出不同的(更改的)值?
我的功能
private gridLoaded(filters: FilteredListingInput): Observable<boolean> {
return this.settings.states.Loaded.pipe(
tap(loaded => {
this.logService.debug(
`Grid ID=<${
this.settings.id
}> this.settings.states.Loaded state = ${loaded}`
);
// Now we get duplicate firings of this action.
if (!loaded) {
this.logService.debug(
`Grid Id=<${
this.settings.id
}> Dispatching action this.settings.stateActions.Read`
);
this.store.dispatch(
new this.settings.stateActions.Read(filters)
);
}
}),
filter(loaded => loaded),
take(1)
);
}
this.settings.states.Loaded
是NgRx存储区中的选择器。
我得到的日志输出看起来像这样:
Grid ID=<grid-reviewItem> this.settings.states.Loaded state = false {ignoreIntercept: true}
Grid Id=<grid-reviewItem> Dispatching action this.settings.stateActions.Read {ignoreIntercept: true}
Grid ID=<grid-reviewItem> this.settings.states.Loaded state = true {ignoreIntercept: true}
Grid ID=<grid-reviewItem> Calling FilterClientSide action. Loaded=true {ignoreIntercept: true}
Grid ID=<grid-reviewItem> this.settings.states.Loaded state = true {ignoreIntercept: true}
Grid ID=<grid-reviewItem> Calling FilterClientSide action. Loaded=true {ignoreIntercept: true}
Grid ID=<grid-reviewItem> this.settings.states.Loaded state = true {ignoreIntercept: true}
Grid ID=<grid-reviewItem> Calling FilterClientSide action. Loaded=true {ignoreIntercept: true}
如何确保相关动作仅触发一次?
编辑-更新
选择代码:
export const getReviewItemsLoaded = createSelector(
getReviewItemState,
fromReviewItems.getReviewItemsLoaded
);
export const getReviewItemState = createSelector(
fromFeature.getForecastState,
(state: fromFeature.ForecastState) => {
return state.reviewItems;
}
);
export const getReviewItemsLoaded = (state: GridNgrxState<ReviewItemListDto>) =>
state.loaded;
export interface GridNgrxState<TItemListDto> {
allItems: TItemListDto[];
filteredItems: TItemListDto[];
totalCount: number;
filters: FilteredListingInput;
loaded: boolean;
loading: boolean;
selectedItems: TItemListDto[];
}
如您所见,我们只是获得了state.loaded
属性,它是一个琐碎的选择器。
loading
属性的Reducers :
export function loadItemsSuccessReducer(state: any, action: GridAction) {
const data = action.payload;
return {
...state,
loading: false,
loaded: true,
totalCount: data.totalCount ? data.totalCount : data.items.length,
allItems: data.items
};
}
export function loadItemsReducer(state: any, action: GridAction) {
return {
...state,
loading: true,
filters: action.payload
};
}
export function loadItemsFailReducer(state: any, action: GridAction) {
return {
...state,
loading: false,
loaded: false
};
}
操作
export class LoadReviewItemsAction implements Action {
readonly type = LOAD_REVIEWITEMS;
constructor(public payload?: FilteredListingInput) {}
}
export class LoadReviewItemsFailAction implements Action {
readonly type = LOAD_REVIEWITEMS_FAIL;
constructor(public payload: any) {}
}
export class LoadReviewItemsSuccessAction implements Action {
readonly type = LOAD_REVIEWITEMS_SUCCESS;
constructor(public payload: PagedResultDtoOfReviewItemListDto) {}
效果
export class ReviewItemsEffects {
constructor(
private actions$: Actions,
private reviewItemApi: ReviewItemApi
) {}
@Effect()
loadReviewItems$ = this.actions$
.ofType(reviewItemActions.LOAD_REVIEWITEMS)
.pipe(
switchMap((action: reviewItemActions.LoadReviewItemsAction) => {
return this.getDataFromApi(action.payload);
})
);
/**
* Retrieves and filters data from API
*/
private getDataFromApi(filters: FilteredListingInput) {
return this.reviewItemApi.getReviewItems(filters || {}).pipe(
map(
reviewItems =>
new reviewItemActions.LoadReviewItemsSuccessAction(
reviewItems
)
),
catchError(error =>
of(new reviewItemActions.LoadReviewItemsFailAction(error))
)
);
}
}
答案 0 :(得分:0)
我可以通过将gridLoaded
方法重构为waitForGridLoaded
并将其某些逻辑移出该方法来解决此问题。效果很好,但我无法解决tap(loaded => ...)
逻辑为何被多次触发的原始问题。
现在相关的位看起来像这样(这似乎不是最好的解决方案):
private initializeLoadingState() {
const loadingStateSubscription = this.settings.states.Loading.subscribe(
loading => {
this.loading = loading;
}
);
this.addSubscription(loadingStateSubscription);
}
private initializeLoadedState() {
const loadedStateSubscription = this.settings.states.Loaded.subscribe(
loaded => {
this.loaded = loaded;
}
);
this.addSubscription(loadedStateSubscription);
}
onLazyLoad(event: LazyLoadEvent) {
// Do nothing yet if we are expecting to set parent filters
// but we have not provided any parent filter yet
if (
this.settings.features.ParentFilters &&
(!this.parentFiltersOnClient ||
!this.parentFiltersOnClient.length) &&
(!this.parentFiltersOnServer || !this.parentFiltersOnServer.length)
) {
return;
}
this.loadAndFilterItems(event);
}
private loadAndFilterItems(event: LazyLoadEvent) {
if (this.settings.features.ClientSideCaching) {
if (this.loaded) {
// Load only once and filter client side
this.store.dispatch(
new this.settings.stateActions.FilterClientSide(
this.buildFilters(event, GridParentFilterTypes.Client)
)
);
} else if (!this.loading) {
// Start loading in from server side
this.store.dispatch(
new this.settings.stateActions.Read(
this.buildFilters(event, GridParentFilterTypes.Server)
)
);
// When we have finished loading, apply any client side filters
const gridLoadedSubscription = this.waitForGridLoaded().subscribe(
loaded => {
if (loaded) {
this.store.dispatch(
new this.settings.stateActions.FilterClientSide(
this.buildFilters(
event,
GridParentFilterTypes.Client
)
)
);
}
}
);
this.addSubscription(gridLoadedSubscription);
}
} else {
this.store.dispatch(
new this.settings.stateActions.Read(
this.buildFilters(event, GridParentFilterTypes.Server)
)
);
}
}
private waitForGridLoaded(): Observable<boolean> {
return this.settings.states.Loaded.pipe(
filter(loaded => loaded),
take(1)
);
}