【问题标题】:Angular NGRX Action Payload Appears to Change Between Dispatch and ImplementAngular NGRX 动作有效负载似乎在调度和实施之间发生变化
【发布时间】:2021-03-25 17:36:40
【问题描述】:

Angular v10、NGRX v10

我有一个 Plan 对象,它的一个属性是一个 Task 对象数组。在我的 TaskService 中,我对我的 Plan 对象进行了深度克隆(lodash),更新了我的 Tasks 中的一些属性,然后调度了一个 Action 来更新我的 Store 中的 Plan。奇怪的是,当我在 Reducer 中 console.log(action.payload) 时,Task 属性已恢复为原始值!看了四天,我就是看不到它。

通过在甘特图上拖动一个任务来启动该过程,然后调用 TaskService.updateTasks(tasksToUpdate) 传递任何在用户交互后发生更改的任务。

我在我的 TaskService 中创建了一个非常简化的 updateTasks 方法版本,但我仍然遇到同样的问题。我的简化updateTasksSimple方法如下:

  public updateTasksSimple(tasks: ITask[]): void {

    const updatedPlan: IPlan = this.coreDataService.cloneDeep(this.currentPlanWithChildren);
    const updatedTasks: ITask[] = this.coreDataService.cloneDeep(tasks);
    const tasksToUpdate = updatedTasks.filter(t => t.state !== objectState.Unchanged);

    // Check we have a tasksToUpdate object.
    if (tasksToUpdate !== null) {

      // Ensure we have some Tasks to update.
      if (tasksToUpdate.length > 0) {

        // Logging
        tasksToUpdate.forEach(t => {
          if (t.state !== objectState.Unchanged) {
            console.log(`Updating Task - ${t.title}, Summary: ${t.summary}, Start: ${t.start}, End: ${t.end}, Duration: ${t.durationHours}, Constraint: ${t.taskConstraintType.name}`);
          }
        });

        // Map updated tasks
        updatedPlan.tasks = tasksToUpdate.map((t: ITask) => ({
          ...t,
          start: new Date(2021, 2, 26, 16),
          end: new Date(2021, 2, 26, 22),
          durationHours: 6,
        }));

        // Logging
        console.log(`updateTasksSimple Tasks before dispatch:`);
        updatedPlan.tasks.forEach(t => {
          console.log(`${t.title}, start: ${t.start}, end: ${t.end}, durationHours: ${t.durationHours}, constraint: ${t.taskConstraintType.name}`);
        })

        // Dispatch an Action to update the Store.
        this.store.dispatch(new planActions.SetCurrentPlanWithChildren(updatedPlan));

      }

    }

  }

我的Action定义如下:

export class SetCurrentPlanWithChildren implements Action {
  readonly type = PlanActionTypes.SetCurrentPlanWithChildren;
  constructor(public payload: IPlan) { }
}

我按如下方式发送我的操作:

  private dispatchUpdate() {

    // Logging
    console.log(`dispatchUpdate called. Tasks:`);
    this.updatedPlan.tasks.forEach(t => {
      console.log(`${t.title}, start: ${t.start}, end: ${t.end}, durationHours: ${t.durationHours}, constraint: ${t.taskConstraintType.name}`);
    })

    this.store.dispatch(new planActions.SetCurrentPlanWithChildren(this.updatedPlan));

  };

如您所见,我正在临时记录任务以准确查看我从 TaskService 调度的内容,这里一切正常。

我的 Reducer 如下:

    case PlanActionTypes.SetCurrentPlanWithChildren:
      console.log(`PlanActionTypes.SetCurrentPlanWithChildren.`, action.payload);
      return {
        ...state,
        currentPlanWithChildren: action.payload,
        error: ''
      };

此处记录的有效负载具有我的 TaskService 进行更改之前的原始值。因此,当我导航到订阅以下选择器的组件时:

export const getCurrentPlanWithChildren = createSelector(
  getPlanFeatureState,
  (state: PlanState) => state.currentPlanWithChildren
);

它不显示更新的值。我很有可能在这里遗漏了一些明显的东西,但我看不到。非常欢迎任何意见或建议。

更新 在进一步调查中,我观察到以下情况:我的应用有 2 个页面;甘特图和任务。如果我导航到甘特图页面并调用我的updateTasksSimple 方法,它会更改我的任务上的startendduration 属性并分派一个操作来更新商店,如上所述。如果我然后导航到订阅 Store 的任务页面,那么 observable 收到的初始任务是旧值。但是,如果我重新加载应用程序,导航到 Gantt 页面并调用我的 updateTasksSimple 方法 TWICE 然后导航到 Tasks 页面并查看我的 console.log (在 observable 上使用 rxjs tap)收到的初始任务是新的价值观。 就好像我的 observable 接收到的初始值是旧值,而不是 Store 中的当前值。我的任务页面声明我的 observable 如下:

  ...
  public currentPlanWithChildren$: Observable<IPlan>;
  ...

  ngOnInit() {

    // Log the Tasks when the CurrentPlanWithChildren changes.
    this.currentPlanWithChildren$ = this.store.pipe(select(fromPlan.getCurrentPlanWithChildren)).pipe(
      tap(currentPlanWithChildren => {
        if (currentPlanWithChildren != null) {
          console.log(`PlanTasksContainerComponent.currentPlanWithChildren changed.`);
          currentPlanWithChildren.tasks.forEach(t => {
            console.log(`Task - ${t.title}, Summary: ${t.summary}, Start: ${t.start.toString()}, End: ${t.end.toString()}, Duration: ${t.durationHours.valueOf()}, Constraint: ${t.taskConstraintType.name}`);
          })
        }
      })
    );

  }

很奇怪。

【问题讨论】:

  • 能否提供TaskService的代码?以及如何以及何时调用 dispatchAction() 方法?
  • 好评。我在TaskService 中重新访问了我的方法并大大简化了它,硬编码了一些日期和持续时间。仍然当我在调度前记录任务时,它们与预期的一样,但在 Reducer 中它们是不正确的!
  • 这真是奇怪的行为。再一次,您是否尝试过 Web 浏览器中的 Redux Dev Tools?您可能会注意到那里发生了一些奇怪的事情。

标签: angular typescript ngrx


【解决方案1】:

似乎有点可疑的是,即使您将currentPlanWithChildren 的深层副本分配给updatedPlan,但您只是通过引用为其分配更新的任务。尝试创建更新任务的副本:

    // Map updated tasks
    updatedPlan.tasks = tasksToUpdate.map(t => ({
      ...t,
      t.start = new Date(2021, 2, 26, 16);
      t.end = new Date(2021, 2, 26, 22);
      t.durationHours = 6;
    }));
    // this is not necessary then
    // updatedPlan.tasks = tasksToUpdate;

【讨论】:

  • 好主意。我实施了您的建议,并修改了上面的代码,但仍然相同。在调度动作之前,任务很好,当在减速器中收到时,值与编辑前一样。奇怪的是,调用我的TaskService 方法并传递已编辑任务的甘特页面也订阅了currentPlanWithChildren(因此对可观察对象的更新反映在甘特图中)并使用正确的值进行更新!但是,导航到也订阅 currentPlanWithChildren 的其他页面会显示旧值。
  • 这很奇怪,你检查过 Redux 开发工具吗?
  • 我添加了一个更新来描述我最近的发现。
【解决方案2】:

经过大量调查,我发现问题是由于我自己对 NGRX 的使用不当造成的。在我的代码的其他地方,我订阅了一个返回 currentPlanWithChildren.tasks 的选择器,这是一个 Observable&lt;ITask[]&gt;。然后我对返回的任务数组进行排序,无意中改变了任务对象,这似乎是我问题的根源。为了解决这个问题,我简单地将我的排序更改为tasks.slice().sort(t =&gt; ...

【讨论】:

    猜你喜欢
    • 2021-06-28
    • 2019-01-24
    • 2020-06-24
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 2022-11-16
    相关资源
    最近更新 更多