ngrx/存储操作已管理多次

问题描述 投票:0回答:1

我正在学习如何使用 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()
    是否被执行了多次。事实并非如此。
  • 不,调度不在订阅或循环内,不会导致其被多次触发。
  • 从选择器生成的可观察对象仅通过模板内的异步管道进行管理。
angular ngrx ngrx-store
1个回答
0
投票

经过 3 天的调试但没有找到任何东西,今天我注意到我用来记录操作的全局元还原器内断点的堆栈跟踪中有一些内容:

这是开发工具。

注释掉它解决了问题。

这提出了很多问题,但现在我很高兴。

© www.soinside.com 2019 - 2024. All rights reserved.