You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular NgRx页面往返后产品列表重复显示问题排查求助

Hey Leonard, let's break down why your product list keeps doubling every time you navigate back to the HomeComponent, and fix it step by step.

What's Causing the Duplication?

Two key issues are at play here:

  1. Your Effect uses mergeMap which doesn't cancel pending requests: If you navigate away and back before the first API call finishes, you'll end up with two successful responses. Each response adds products to your state, leading to duplicated items.
  2. Your Reducer uses addMany instead of replacing the full list: Even though you're clearing entities manually, addMany is designed to append to existing entities—not replace them. Combined with pending requests, this makes duplication inevitable. Also, your optional id in the Product interface can confuse NgRx's EntityAdapter, which relies on unique IDs to manage entities correctly.

Step-by-Step Fixes

1. Replace mergeMap with switchMap in Your Effect

switchMap cancels any pending API requests when a new LoadProducts action is dispatched. This ensures only the latest request's response updates your state, eliminating duplicate API responses.

Update your effect.ts:

@Effect() loadproducts$: Observable<Action> = this.action$.pipe(
  ofType<productActions.LoadProducts>(
    productActions.UserActionTypes.LOAD_CREATED_PRODUCTS
  ),
  switchMap((actions: productActions.LoadProducts) => // Changed mergeMap to switchMap
    this.product_service.getCreatedProducts().pipe(
      map((products: Product[]) =>
        new productActions.LoadProductsSuccess(products)
      ),
      catchError(err => of(new productActions.LoadProductsFail(err)))
    ))
)

2. Use setAll Instead of addMany in Your Reducer

setAll replaces all existing entities in the state with the new product list—this is exactly what you want when loading a full list of products. You no longer need to manually clear entities either, since setAll handles that automatically.

Update the success case in product.reducer.ts:

case productActions.UserActionTypes.LOAD_CREATED_PRODUCTS_SUCCESS: {
  return productAdapter.setAll(action.payload, {
    ...state,
    loaded: true,
    loading: false,
    error: "" // Reset error state on successful load
  });
}

3. Make id a Required, Unique Field

EntityAdapter needs a unique ID to track entities properly. Your current Product interface has an optional id, which can lead to unexpected duplication if IDs are missing or undefined. Update your interface to make id required (assuming your API returns unique IDs for each product):

export interface Product{ 
  id:string, // Remove the ? to make it required
  name:string, 
  price:string; 
  quantity:string; 
  tags:Tags[]; 
  description:string; 
  files: File[]; 
}

If your API doesn't return IDs, you can generate unique client-side IDs (e.g., using uuid) before dispatching the LoadProductsSuccess action, but server-provided unique IDs are always preferable.

Why These Fixes Work

  • switchMap ensures only the most recent API response is processed, so no duplicate product batches are added to the state.
  • setAll guarantees the state always holds the latest full list of products, even if edge cases slip through.
  • A required id lets EntityAdapter correctly manage entities and avoid accidental duplicates.

内容的提问来源于stack exchange,提问作者Leonard Loss

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 07:17:33