ngrx 8:从 reducer 函数访问 @ngrx/data 实体的状态
ngrx 8: Access state of @ngrx/data entity from reducer function
我需要在 reducer 函数中访问 ngrx/data 实体的状态。
我正在构建一个寻呼机(分页),用户可以在其中导航到最后一页以及其他选项。但我不知道缓存中有多少项,EntitySelectorsFactory
确实(selectCount
),但我不想在组件中获取数据并使用 props 传递它们,相反,它们应该在减速器中。
或者也许有更好的方法。
操作
import {createAction, props} from '@ngrx/store';
export const toStart = createAction('[Pager] To Start');
export const toEnd = createAction('[Pager] To End');
export const incrementPage = createAction('[Pager] Increment');
export const decrementPage = createAction('[Pager] Decrement');
export const setPage = createAction('[Pager] Set', props<{page: number}>());
export const setPageSize = createAction('[Pager] Set Page Size', props<{size: number}>());
减速器
import { Action, createReducer, on } from '@ngrx/store';
import * as PagerActions from '../actions/pager';
import {EntitySelectorsFactory} from '@ngrx/data';
import {Rant} from '../models/rant';
export const pagerFeatureKey = 'pager';
export interface State {
page: number;
pageSize: number;
}
export const initialState: State = {
page: 1,
pageSize: 10,
};
const rantSelectors = new EntitySelectorsFactory().create<Rant>('Rant');
const pagerReducer = createReducer(
initialState,
on(PagerActions.toStart, state => ({...state, page: 1})),
on(PagerActions.toEnd, state => ({...state, page: Math.floor(rantSelectors.selectEntities.length / state.pageSize)})),
on(PagerActions.setPage, (state, action) => ({...state, page: action.page})),
on(PagerActions.incrementPage, state => ({...state, page: state.page + 1})), // TODO: check if out of bounds
on(PagerActions.decrementPage, state => ({...state, page: state.page > 1 ? state.page - 1 : 1})),
on(PagerActions.setPageSize, (state, action) => ({...state, pageSize: action.size}))
);
export function reducer(state: State | undefined, action: Action) {
return pagerReducer(state, action);
}
无论我在
做什么
on(PagerActions.toEnd, state => ({...state, page: Math.floor(rantSelectors.selectEntities.length / state.pageSize)})),
始终为 0。rantSelectors.selectCount.length
或以上。可能是因为长度为0。
我还需要知道 increment
reducer 中 "rant" 实体的长度或数量。
我不知道如何获取实际值。
我在想...有一个实体计数选择器并使用道具在 toEnd
操作中传递该计数。另一种选择是副作用,但它们令人困惑,恕我直言,这不是编写代码的正确方法(不可读的代码,立即 cause/effect 丢失,是的,我知道,我不想争论它)。然后即使产生了副作用,人们又如何在模块中定义它呢?它是 AppEffects
副作用还是明显的副作用?我猜是 AppEffects,因为它不在另一个模块中。
app.module.ts
EffectsModule.forRoot([AppEffects]),
AppEffects = 几乎是空的,默认 ng add
内容。
所以是的,我需要在 reducer 中获取 @ngrx/data 实体的状态,获取其实际值,或者更优雅的方法。
这是一个没有直接回答问题的答案。相反,我采用了不同的方法,最初是第一种方法。它不是那么优雅,但为了可重用性,我会将这些功能放入单独的服务中。
函数
this.maxPages$ = this.store.select(selectMaximumRantPages);
toStart() {
this.store.dispatch(PagerActions.setPage({page: 1}));
}
toEnd() {
this.maxPages$.subscribe(pages => {
this.store.dispatch(PagerActions.setPage({page: pages}));
}).unsubscribe();
}
setPage(page: number) {
this.store.dispatch(PagerActions.setPage({page}));
}
setPageSize(size: number) {
this.store.dispatch(PagerActions.setPageSize({size}));
}
increment() {
this.store.dispatch(PagerActions.incrementPage());
}
decrement() {
this.store.dispatch(PagerActions.decrementPage());
}
真的只需要 getter 和 setter。便利功能似乎在 ngrx 世界中没有空间。我还将摆脱递增和递减操作并将它们实现为服务方法。
选择器
export const selectPagerPage = (state: State) => state.pager.page;
export const selectPagerPageSize = (state: State) => state.pager.pageSize;
export const selectMaximumRantPages = createSelector(
selectPagerPageSize,
rantSelectors.selectCount,
(size, count) => Math.ceil(count / size)
);
我更愿意保持我的组件清洁...我看到了两种解决您问题的方法
- 您可以将
rantSelectors.selectEntities.length
的长度存储在存储的这一部分,方法是向您的状态添加一个数值。当您 load/update rantSelectors.selectEntities
时,您也会更新商店的这一部分。
export interface State {
page: number;
pageSize: number;
rantEntitiesSize: number;
// ...
on(rantActions.loadSuccess,
rantActions.UpdateSuccess, //could include adding objects to the current loaded list
rantActions.RemoveSuccess, //(negative number)
(state, action) =>
({...state, rantEntitiesSize: (state.rantEntitiesSize + action)}),
}
- 你可以保留
toEnd action
而不是在 reducer 中使用它,而是构建一个在 toEnd action
上调用的效果,它从商店的其他部分获取你需要的值,然后调用将值传递给减速器的第二个操作。
toEndPager$ = createEffect(() =>
this.actions$.pipe(
ofType(PagerActions.toEnd),
withLatestFrom(this.store.select(rantSelectors.selectEntities.length)),
switchMap(([{},length]) => {
return of(PagerActions.toEndSuccess({ length });
})
)
);
我需要在 reducer 函数中访问 ngrx/data 实体的状态。
我正在构建一个寻呼机(分页),用户可以在其中导航到最后一页以及其他选项。但我不知道缓存中有多少项,EntitySelectorsFactory
确实(selectCount
),但我不想在组件中获取数据并使用 props 传递它们,相反,它们应该在减速器中。
或者也许有更好的方法。
操作
import {createAction, props} from '@ngrx/store';
export const toStart = createAction('[Pager] To Start');
export const toEnd = createAction('[Pager] To End');
export const incrementPage = createAction('[Pager] Increment');
export const decrementPage = createAction('[Pager] Decrement');
export const setPage = createAction('[Pager] Set', props<{page: number}>());
export const setPageSize = createAction('[Pager] Set Page Size', props<{size: number}>());
减速器
import { Action, createReducer, on } from '@ngrx/store';
import * as PagerActions from '../actions/pager';
import {EntitySelectorsFactory} from '@ngrx/data';
import {Rant} from '../models/rant';
export const pagerFeatureKey = 'pager';
export interface State {
page: number;
pageSize: number;
}
export const initialState: State = {
page: 1,
pageSize: 10,
};
const rantSelectors = new EntitySelectorsFactory().create<Rant>('Rant');
const pagerReducer = createReducer(
initialState,
on(PagerActions.toStart, state => ({...state, page: 1})),
on(PagerActions.toEnd, state => ({...state, page: Math.floor(rantSelectors.selectEntities.length / state.pageSize)})),
on(PagerActions.setPage, (state, action) => ({...state, page: action.page})),
on(PagerActions.incrementPage, state => ({...state, page: state.page + 1})), // TODO: check if out of bounds
on(PagerActions.decrementPage, state => ({...state, page: state.page > 1 ? state.page - 1 : 1})),
on(PagerActions.setPageSize, (state, action) => ({...state, pageSize: action.size}))
);
export function reducer(state: State | undefined, action: Action) {
return pagerReducer(state, action);
}
无论我在
做什么 on(PagerActions.toEnd, state => ({...state, page: Math.floor(rantSelectors.selectEntities.length / state.pageSize)})),
始终为 0。rantSelectors.selectCount.length
或以上。可能是因为长度为0。
我还需要知道 increment
reducer 中 "rant" 实体的长度或数量。
我不知道如何获取实际值。
我在想...有一个实体计数选择器并使用道具在 toEnd
操作中传递该计数。另一种选择是副作用,但它们令人困惑,恕我直言,这不是编写代码的正确方法(不可读的代码,立即 cause/effect 丢失,是的,我知道,我不想争论它)。然后即使产生了副作用,人们又如何在模块中定义它呢?它是 AppEffects
副作用还是明显的副作用?我猜是 AppEffects,因为它不在另一个模块中。
app.module.ts
EffectsModule.forRoot([AppEffects]),
AppEffects = 几乎是空的,默认 ng add
内容。
所以是的,我需要在 reducer 中获取 @ngrx/data 实体的状态,获取其实际值,或者更优雅的方法。
这是一个没有直接回答问题的答案。相反,我采用了不同的方法,最初是第一种方法。它不是那么优雅,但为了可重用性,我会将这些功能放入单独的服务中。
函数
this.maxPages$ = this.store.select(selectMaximumRantPages);
toStart() {
this.store.dispatch(PagerActions.setPage({page: 1}));
}
toEnd() {
this.maxPages$.subscribe(pages => {
this.store.dispatch(PagerActions.setPage({page: pages}));
}).unsubscribe();
}
setPage(page: number) {
this.store.dispatch(PagerActions.setPage({page}));
}
setPageSize(size: number) {
this.store.dispatch(PagerActions.setPageSize({size}));
}
increment() {
this.store.dispatch(PagerActions.incrementPage());
}
decrement() {
this.store.dispatch(PagerActions.decrementPage());
}
真的只需要 getter 和 setter。便利功能似乎在 ngrx 世界中没有空间。我还将摆脱递增和递减操作并将它们实现为服务方法。
选择器
export const selectPagerPage = (state: State) => state.pager.page;
export const selectPagerPageSize = (state: State) => state.pager.pageSize;
export const selectMaximumRantPages = createSelector(
selectPagerPageSize,
rantSelectors.selectCount,
(size, count) => Math.ceil(count / size)
);
我更愿意保持我的组件清洁...我看到了两种解决您问题的方法
- 您可以将
rantSelectors.selectEntities.length
的长度存储在存储的这一部分,方法是向您的状态添加一个数值。当您 load/updaterantSelectors.selectEntities
时,您也会更新商店的这一部分。
export interface State {
page: number;
pageSize: number;
rantEntitiesSize: number;
// ...
on(rantActions.loadSuccess,
rantActions.UpdateSuccess, //could include adding objects to the current loaded list
rantActions.RemoveSuccess, //(negative number)
(state, action) =>
({...state, rantEntitiesSize: (state.rantEntitiesSize + action)}),
}
- 你可以保留
toEnd action
而不是在 reducer 中使用它,而是构建一个在toEnd action
上调用的效果,它从商店的其他部分获取你需要的值,然后调用将值传递给减速器的第二个操作。
toEndPager$ = createEffect(() =>
this.actions$.pipe(
ofType(PagerActions.toEnd),
withLatestFrom(this.store.select(rantSelectors.selectEntities.length)),
switchMap(([{},length]) => {
return of(PagerActions.toEndSuccess({ length });
})
)
);