在redux状态下更新项目导致无限循环/浏览器锁定

时间:2018-07-31 10:34:54

标签: angular typescript redux ngrx ngrx-store

预期的结果:我正在尝试更新Redux状态的值

问题::我最终遇到了无限循环/浏览器锁定的问题。我已经读过this 'SO' postdocs,但努力查看我要去哪里。

这是我的状态:

{ id: 0, product: TV, saleItem: false },
{ id: 1, product: Fridge, saleItem: false }

我想将其更新为

{ id: 0, product: TV, saleItem: true }
{ id: 1, product: Fridge, saleItem: false }

我的网址是:localhost:4200 /#/ 0

我正在使用选择器来获取商店中的所有商品,检查url参数并以状态返回商品。上面的网址将返回{ id: 0, product: TV, saleItem: false } 然后,我运行item = { ...item, saleItem: true };以使减速器起火。但是,这会导致无限循环,其中console.log('before', item);console.log('after', item);一遍又一遍地注销。以下是我拥有的代码以及我尝试过的一些替代方法

选择器

export const getBasketEntities = createSelector(
  getBasketState,
  fromItems.getBasketEntities
);

export const getSelectedItem = createSelector(
  getBasketEntities,
  fromRoot.getRouterState,
  (entities, router): Item => {
    return router.state && entities[router.state.params.id];
  }
);

组件

this.store.dispatch(new fromStore.UpdateItem());

动作

export class UpdateItem implements Action {
  readonly type = UPDATE_ITEM;
  constructor() {}
}

效果

// update effect
@Effect()
updateItem$ = this.actions$.ofType(itemActions.UPDATE_ITEM).pipe(
  switchMap(() => {
    return this.store.select(fromSelectors.getSelectedItem).pipe(
      map((item: Item) => {
        console.log('before', item);
        item = { ...item, saleItem: true };
        console.log('after', item);
        return new itemActions.UpdateItemSuccess(item);
      }),
      catchError(error => of(new itemActions.UpdateItemFail(error)))
    );
  })
);

减速器

case fromItems.UPDATE_ITEM_SUCCESS: {
  const item: Item = action.payload;
  console.log('reducer', item);

  const entities = {
    ...state.entities,
    [item.id]: item
  };

  return {
    ...state,
    entities
  };
}

更新:

  • 从效果中删除了选择器。
  • 调用选择器并将值传递给操作(有效载荷)
  • 在化简器中更新项目

这将导致相同的结果。

组件

onUpdate() {
  this.store
    .select(fromStore.getSelectedItem)
    .pipe(
      map((item: Item) => {
        this.store.dispatch(new fromStore.UpdateItem(item));
      })
    )
    .subscribe()
    .unsubscribe();

}

效果

@Effect()
  updateItem$ = this.actions$.ofType(itemActions.UPDATE_ITEM).pipe(
  map((action: itemActions.UpdateItem) => action.payload),
  map((item: Item) => {
    return new itemActions.UpdateItemSuccess(item);
  }),
  catchError(error => of(new itemActions.UpdateItemFail(error)))
);

动作

export class UpdateItem implements Action {
  readonly type = UPDATE_ITEM;
  constructor(public payload: Item) {}
}

减速器

case fromItems.UPDATE_ITEM_SUCCESS: {
  const item: Item = action.payload;

  const entities = {
    ...state.entities,
    [item.id]: { ...item, saleItem: true }
  };
  return {
    ...state,
    entities
  };
}

2 个答案:

答案 0 :(得分:0)

出现无限循环的原因:

您订阅选择器->效果是创建新引用并修改项目->更新状态->这将触发选择器->依此类推……

要解决此问题:

  • 请勿在您的效果内更新项目。
  • 在更新操作中将所选项目设置为有效载荷
  • 更新减速器中的项目(您的示例中无需执行成功或失败操作)

答案 1 :(得分:0)

修复了以下问题,但这一点感觉都不好。 最好是我的效果文件中只需要一行与调度和选择器一起使用。 我在选择器上调查了withLatestFrom(),以获得更干净的解决方案

更新:或者,我发现并使用了以下讨论:on gihubstackblitz。我使用了选项2,并将do替换为map。这意味着我可以在组件this.store.dispatch(new fromStore.UpdateItem());中运行一行,然后使用[item.id]: { ...state.entities[item.id], saleItem: true }

在reducer中设置项目替换。

初始修复

this.selectedItem: Item;

onUpdate() {
   this.store
     .select(fromStore.getSelectedItem)
     .subscribe((item: Item) => this.selectedItem = item )
     .unsubscribe();
   this.store.dispatch(new fromStore.UpdateItem(this.selectedItem));
 }

StackBlitz方法1 -将tap替换为map。清洁工

@Effect()
  updateItem$ = this.actions$.ofType(itemActions.UPDATE_ITEM).pipe(
    withLatestFrom(this.store.pipe(select(fromSelectors.getSelectedItem))),
    map(([type, item]: Array<any>) => {
      console.log('in effect', item);
      return new itemActions.UpdateItemSuccess(item);
    }),
    catchError(error => of(new itemActions.UpdateItemFail(error)))
  );