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

Angular NgRx effects获取数据报错 无法读取undefined的products属性

NgRx拉取API数据报错Cannot read properties of undefined (reading 'products')排查

ERROR TypeError: Cannot read properties of undefined (reading 'products')
触发场景:未使用NgRx特性模块,调用示例API获取商品数据时抛出上述错误。


问题关联代码

Selector 选择器

export const selectProductState = createFeatureSelector<fromProduct.ProductState>(
    fromProduct.customerFeatureKey
);

export const getProductState = createSelector(
    selectProductState,
      (state: fromProduct.ProductState) => state
);

Reducer 定义

import { createReducer, on } from "@ngrx/store";
import { Product } from "../product";
import * as ProductAction from './product.actions';

export interface ProductState {
    products: Product[];
    error: string;
  }

  export const customerFeatureKey = 'products';
  
  export const initialState: ProductState = {
    products: [],
    error: ''
  };

  export const productReducer = createReducer<ProductState>(
    initialState,
    on(ProductAction.loadProductsSuccess, (state, action): ProductState => {
      return {
        ...state,
        products: action.products,
        error: ''
      };
    }),
    on(ProductAction.loadProductsFailure, (state, action): ProductState => {
      return {
        ...state,
        products: [],
        error: action.error
      };
    })
  );

Effects 定义

@Injectable()
export class ProductEffects {

  constructor(private actions$: Actions, private productService: ProductService) { }

  loadProducts$ = createEffect((): any => {
    return this.actions$.pipe(
        ofType(ProductAction.loadProducts),
        mergeMap(()=> this.productService.getProducts().pipe(
            map(products => 
              ProductAction.loadProductsSuccess({products})
            )
        ))
    )
  })
}

错误根因与修复方案

报错核心原因是createFeatureSelector在全局状态树里找不到products对应的状态切片,拿到undefined后访问其products属性直接触发类型错误,本质是reducer未正确注册,附带两个其他隐性问题,按以下顺序修复即可:

  • 根模块正确注册Reducer和Effects
    没有使用特性模块的场景下,必须在根模块AppModule的StoreModule.forRoot()中显式注册productReducer,注册的key必须和定义的customerFeatureKey(值为'products')完全一致,同时要在EffectsModule.forRoot()中注册你写的ProductEffects,否则action触发后不会执行接口请求逻辑:
    // 替换为你项目中对应文件的实际路径
    import { productReducer, customerFeatureKey } from './products/state/product.reducer';
    import { ProductEffects } from './products/state/product.effects';
    
    @NgModule({
      imports: [
        // 其他已有模块
        StoreModule.forRoot({
          [customerFeatureKey]: productReducer
        }),
        EffectsModule.forRoot([ProductEffects]),
        // 其他已有模块
      ]
    })
    export class AppModule {}
    
  • 补全Effect的错误捕获逻辑
    当前Effect没有处理接口请求失败的场景,一旦请求报错会直接中断整个Effect流,后续再触发loadProducts动作不会有任何响应,补充catchError逻辑派发失败动作:
    loadProducts$ = createEffect(() => {
      return this.actions$.pipe(
          ofType(ProductAction.loadProducts),
          mergeMap(()=> this.productService.getProducts().pipe(
              map(products => ProductAction.loadProductsSuccess({products})),
              catchError(error => of(ProductAction.loadProductsFailure({error})))
          ))
      )
    })
    
    去掉createEffect的返回值类型声明any,TS会自动做类型校验,避免低级语法错误。
  • 冗余选择器优化(可选)
    定义的getProductState完全冗余,selectProductState本身就会返回整个ProductState切片,不需要额外套一层createSelector。如果需要直接取商品列表、错误信息,可以单独定义细粒度选择器:
    export const selectAllProducts = createSelector(
      selectProductState,
      (state) => state.products
    )
    export const selectProductError = createSelector(
      selectProductState,
      (state) => state.error
    )
    

修复完成后,初始状态下products字段就是reducer中定义的空数组,不会再出现state为undefined的问题,接口请求成功后数据会正常写入状态,选择器可正常读取对应字段。


内容的提问来源于stack exchange,提问作者Nagarjuna Reddy

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:09:27