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
相关产品推荐
相关产品推荐

