rxjs - 仅从 ngrx 存储选择器中获取不同的值
问题描述
我有一个函数可以检查网格是否已加载,如果没有则触发加载。但目前这最终会为相同的值触发多次,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
属性的减速器:
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))
)
);
}
}
解决方案
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)
);
}
推荐阅读
- html - 将自定义 CSS 添加到 Wordpress 仪表板
- java - 将文件上传到 blob 容器似乎会泄漏通道
- postgresql - 使用 PostgreSQL 时单元测试产生不同的结果
- c++ - 为什么 C++ 允许前向声明类而不是枚举?
- android - Android 应用选择性地显示谷歌地图
- git - git 由于 --skip-worktree 更改而阻止结帐
- python - 为什么tableView中列的宽度没有变化
- python - Python 格式百分比
- azure - 在 ARM 模板中指定资源组以创建 Azure 开发测试实验室 VM
- asp.net-core - EF Core 有连接池吗?