我正在学习如何使用 ngrx/entity,并且我开发了一个可以管理一些实体的“简单”应用程序:
我有一份部门列表,每个部门可以有一个或多个区域。
因此,我创建了管理多个实体及其关系所需的一切:
索引.ts
export const mainFeatureKey = 'main';
export interface MainState {
[fromDepartment.departmentFeatureKey]: fromDepartment.DepartmentState;
[fromArea.areaFeatureKey]: fromArea.AreaState;
[fromArea.departmentAreaFeatureKey]: fromArea.DepartmentAreaState;
}
export const reducers: ActionReducerMap<MainState> = {
[fromDepartment.departmentFeatureKey]: fromDepartment.reducer,
[fromArea.areaFeatureKey]: fromArea.reducer,
[fromArea.departmentAreaFeatureKey]: fromArea.departmentAreaReducer,
};
export const selectMainState = createFeatureSelector<MainState>(mainFeatureKey);
export const selectDepartmentState = createSelector(
selectMainState,
state => state[fromDepartment.departmentFeatureKey]
);
export const selectAreaState = createSelector(
selectMainState,
state => state[fromArea.areaFeatureKey]
);
export const selectDepartmentAreaState = createSelector(
selectMainState,
state => state[fromArea.departmentAreaFeatureKey]
);
export const selectDepartments = createSelector(selectMainState, state =>
fromDepartment.adapter.getSelectors().selectAll(state[fromDepartment.departmentFeatureKey])
);
export const selectSelectedDepartment = createSelector(
selectDepartmentState,
selectDepartments,
(departmentState, departments) => {
return departments.find(c => c.id === departmentState.selectedCoxtextId);
}
);
export const selectAreas = createSelector(selectMainState, state =>
fromArea.adapter.getSelectors().selectAll(state[fromArea.areaFeatureKey])
);
export const selectSelectedArea = createSelector(
selectAreaState,
selectAreas,
(areaState, areas) => {
return areas.find(a => a.id === areaState.selectedAreaId);
}
);
export const selectDepartmentAreas = createSelector(
selectDepartmentAreaState,
selectSelectedDepartment,
selectAreas,
(state, selectedDepartment, areas) => {
const departmentArea = fromArea.departmentAreaAdapter
.getSelectors()
.selectAll(state)
.find(ca => ca.departmentId === selectedDepartment?.id);
return areas.filter(a => departmentArea?.areaIds.includes(a.id));
}
);
部门.reducer.ts
export const departmentFeatureKey = 'departments';
export interface DepartmentState extends EntityState<Department> {
selectedCoxtextId: number | undefined;
}
// In questo caso sarebbe facoltativo perché l'identificativo di Department si chiama già id
const selectId = (department: Department) => department.id;
const sortByid = (a: Department, b: Department) => a.id - b.id;
export const adapter = createEntityAdapter<Department>({ selectId, sortComparer: sortByid });
const initialState: DepartmentState = adapter.getInitialState({
selectedCoxtextId: undefined,
});
export const reducer = createReducer(
initialState,
on(DepartmentActions.saveDepartments, (state, action): DepartmentState => {
return adapter.setAll(action.departments, {
...state,
selectedCoxtextId: state.selectedCoxtextId,
});
}),
on(
DepartmentActions.selectDepartment,
(state, action): DepartmentState => ({ ...state, selectedCoxtextId: action.departmentId })
)
);
部门.action.ts
export const DepartmentActions = createActionGroup({
source: 'MainModule',
events: {
'Load departments': emptyProps(),
'Save departments': props<{ departments: Department[] }>(),
'Select department': props<{ departmentId: number }>(),
},
});
部门.effect.ts
@Injectable()
export class DepartmentEffects {
loadDepartment$ = createEffect(() =>
this.actions$.pipe(
ofType(DepartmentActions.loadDepartments),
concatLatestFrom(() => this.state.select(selectDepartments)),
filter(([, departments]) => departments.length === 0),
tap(() => debug('Loading departments')),
exhaustMap(() =>
this.dataService
.loadDepartments()
.pipe(map(departments => DepartmentActions.saveDepartments({ departments })))
)
)
);
constructor(
private actions$: Actions,
private state: Store<MainState>,
private dataService: DataService
) {}
}
area.reducer.ts(还包括部门-区域关系减速器)
export const areaFeatureKey = 'areas';
export interface AreaState extends EntityState<Area> {
selectedAreaId: number | undefined;
}
const sortByName = (a: Area, b: Area) => a.name.toLowerCase().localeCompare(b.name.toLowerCase());
export const adapter = createEntityAdapter<Area>({ sortComparer: sortByName });
const initialState: AreaState = adapter.getInitialState({ selectedAreaId: undefined });
export const reducer = createReducer(
initialState,
on(AreaActions.saveAreas, (state, action): AreaState => {
return adapter.addMany(action.areas, { ...state, selectedAreaId: state.selectedAreaId });
})
);
// Relations
export const departmentAreaFeatureKey = 'departmentAreas';
export interface DepartmentAreaState extends EntityState<DepartmentArea> {}
export const departmentAreaAdapter = createEntityAdapter<DepartmentArea>();
export const departmentAreaReducer = createReducer(
departmentAreaAdapter.getInitialState(),
on(AreaActions.saveDepartmentAreas, (state, action): DepartmentAreaState => {
return departmentAreaAdapter.addOne(
{
id: createId(),
departmentId: action.departmentId,
areaIds: action.areas.map(a => a.id),
},
state
);
})
);
区域.action.ts
export const AreaActions = createActionGroup({
source: 'MainModule',
events: {
'Load areas': props<{ departmentId: number }>(),
'Save areas': props<{ departmentId: number; areas: Area[] }>(),
'Save department areas': props<{ departmentId: number; areas: Area[] }>(),
'Select area': props<{ areaId: number }>(),
},
});
区域.效果.ts
@Injectable()
export class AreaEffects {
loadAreas$ = createEffect(() =>
this.actions$.pipe(
ofType(AreaActions.loadAreas),
tap(() => debug('Loading areas')),
exhaustMap(action =>
this.dataService
.loadAreas(action.departmentId)
.pipe(map(areas => AreaActions.saveAreas({ departmentId: action.departmentId, areas })))
)
)
);
saveDepartmentAreas$ = createEffect(() =>
this.actions$.pipe(
ofType(AreaActions.saveAreas),
tap(() => debug('Saving department areas')),
map(action =>
AreaActions.saveDepartmentAreas({ departmentId: action.departmentId, areas: action.areas })
)
)
);
constructor(
private actions$: Actions,
private store: State<MainState>,
private dataService: DataService
) {}
}
当我执行操作(任何操作,甚至从其他功能状态)的调度时,它会被管理两次,而效果调度的操作会被正确管理(但在我从
切换到
exhaustMap
之前,它们也被执行了两次) mergeMap
我不知道这是否是正确的做法)。
我知道问题出在选择器的减速器中,因为当只有部门时它们被触发一次,但我可以理解在哪里。
这是控制台日志:
但是,redux 日志非常不同:
在你提问之前:
dispatch()
是否被执行了多次。事实并非如此。