【问题标题】:Why is my ngrx action causing an endless loop?为什么我的 ngrx 操作会导致无限循环?
【发布时间】:2020-01-07 01:18:03
【问题描述】:

我有一个简单的操作,我想在我的状态下将一些对象添加到数组中。不知何故,它陷入了无限循环。我还不知道为什么会这样。我是 ngrx 和 Angular 的新手。我究竟做错了什么?点击按钮后触发动作,在shelf-item.component.ts中调用下面的函数onAddToCartButtonClicked()

这是我在reducer.ts中的reducer:

export type ShoppinCartState = {
  CartIsOpen: boolean;
  Entries: ShoppingCartEntry[];
};


export function shoppingCartReducer(
  state: ShoppinCartState = { CartIsOpen: false, Entries: [] },
  action: ShoppingCartAction
) {
  switch (action.type) {
    case "CART_TOGGLE":
      return {
        ...state,
        CartIsOpen: !state.CartIsOpen
      };
    case "CART_CLOSE":
      return {
        ...state,
        CartIsOpen: false
      };
    case "CART_ADD_ENTRY":
      return {
        ...state,
        Entries: [...state.Entries.concat(action.payload)]
      };
    default:
      return state;
  }
}

这基本上是我的shelf-item-component.ts

export class ShelfItemComponent implements OnInit {
  @Input() product: Product;
  selectedOption: ProductVariant;
  isInputMissing: boolean = false;

  constructor(private store: Store<State>) {}

  ngOnInit() {
    if (this.product && this.product.variations) {
      if (this.product.variations.length === 1) {
        this.selectedOption = this.product.variations[0];
      }
    }
  }

  onAddToCartButtonClicked() {
    if (this.isValid()) {
      this.addProductToCart();
    }
  }

  addProductToCart() {
    this.store.dispatch({ type: "SET_LOADING" });
    this.store
      .select(state => state.shoppingCartReducer.Entries)
      .subscribe(data => this.dispatchNewCartEntry(data));
  }

  dispatchNewCartEntry(entries: ShoppingCartEntry[]) {
    this.store.dispatch(
      new AddShoppingCartEntry(this.constructNewCartEntry(entries))
    );
    this.store.dispatch({ type: "UNSET_LOADING" });
  }

  constructNewCartEntry(entries: ShoppingCartEntry[]): ShoppingCartEntry {
    let matchingEntry = entries.find(
      entry =>
        entry.product.id === this.product.id &&
        entry.variation === this.selectedOption
    );
    let amount = matchingEntry ? matchingEntry.amount + 1 : 1;
    return {
      product: this.product,
      amount: amount,
      variation: this.selectedOption
    };
  }

  isValid(): boolean {
    if (this.isOptionToBeSelected()) {
      if (this.selectedOption) {
        this.isInputMissing = false;
        return true;
      } else {
        this.isInputMissing = true;
        return false;
      }
    }
    return true;
  }

  isOptionToBeSelected(): boolean {
    if (this.product && this.product.variations.length > 0) {
      return true;
    } else {
      return false;
    }
  }

  onOptionSelected(option: ProductVariant) {
    this.selectedOption = option;
    this.isInputMissing = false;
  }
}

那是我的shelf-item-component.html

<h3>{{ product.title }}</h3>
<img *ngIf="!product.image" src="../../../assets/img/esansBottles Kopie.png" />
<div
  [ngClass]="{ inValid: isInputMissing }"
  *ngIf="product.variations.length > 1"
>
  <form>
    <div class="variation" *ngFor="let variation of product.variations">
      <label>
        <input
          type="radio"
          name="variation"
          value="variation"
          (change)="onOptionSelected(variation)"
        />
        {{ variation.option }} {{ variation.price }} €</label
      >
    </div>
  </form>
</div>
<div *ngIf="product.variations.length === 1">
  <div *ngIf="selectedOption">
    {{ selectedOption.option }} {{ selectedOption.price }}€
  </div>
</div>
<div>
  <p>{{ product.description }}</p>
</div>
<div (click)="onAddToCartButtonClicked()" class="addToCartButton">
  In den Warenkorb
</div>

这是创建有问题组件的父组件shelf.component.html:

<div class="container">
  <div *ngFor="let product of products | async">
    <app-shelf-item [product]="product"></app-shelf-item>
  </div>
</div>

它是shelf.component.ts file

export class ShelfComponent implements OnInit {
  @Input() products: Observable<Product[]>;

  constructor(private store: Store<State>) {}

  ngOnInit() {}
}

我真的被卡住了,不明白:/

【问题讨论】:

    标签: angular typescript ngrx


    【解决方案1】:

    正如 Fateh 提到的,它将被触发并且值将被订阅。如果要在订阅商店选择器时触发此操作,您可以将上述语句链接到 RxJS take() 运算符。

    此运算符将确保只有提供给 take() 运算符的指定计数值将由源 observable 发出。

    import { take } from 'rxjs/operators';
    
    this.store
      .pipe(
        select(state => state.shoppingCartReducer.Entries}),
        take(1),
      ).subscribe(data => {
        this.dispatchNewCartEntry(data);
      });
    

    【讨论】:

    • 非常感谢。 pipe 和 take(1) 似乎很有趣。它在第一次尝试时不起作用。我想我必须安装 RxJS 才能使用它。我只是将列表订阅调用提取到构造函数并将其保存到局部变量中,稍后我将在 onClick 处理程序中使用该变量。似乎也能正常工作...
    • 您正在使用 Angular,不是吗? RxJS 默认安装。您只需要将 take 操作符导入到您的组件中! import { take } from 'rxjs/operators;'. You will need to add this somewhere along the top of your file
    • 啊好吧...不知道...我稍后再试
    • ` this.store .pipe( select(state => state.shoppingCartReducer.Entries), take(1) ) .subscribe(data => this.dispatchNewCartEntry(data));`
    • 太棒了!很高兴为您提供帮助!
    【解决方案2】:

    正常,这里订阅购物车实体

    this.store
      .select(state => state.shoppingCartReducer.Entries)
      .subscribe(data => this.dispatchNewCartEntry(data));
    

    这意味着每次购物车条目发生变化时,都会在订阅内触发调度

    【讨论】:

    • 谢谢法塔赫。不知道我怎么看不到。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-06
    相关资源
    最近更新 更多