Angular 17 + NgRx 页面切换时dispatch触发‘Cannot read properties of null’错误
问题分析与解决方案
1. 修正状态选择器,关联当前Request ID
你的selectIfRequestLifeCycleListLoaded是全局判断列表加载状态,切换不同Request ID时,无法区分当前ID的数据是否就绪。需要修改选择器,检查当前Request ID对应的数据是否存在且已加载:
// 在requestLifeCycleSelectors中新增选择器 export const selectRequestLifeCycleById = createSelector( selectRequestLifeCycleState, (state, props: { requestId: string }) => state.entities[props.requestId] ); export const selectIsRequestLifeCycleLoadedById = createSelector( selectRequestLifeCycleById, (request) => !!request );
然后更新Guard的checkStore方法,根据当前requestId监听对应状态:
checkStore(requestId: string): Observable<boolean> { // 先检查是否已有该ID的数据,避免重复请求 const hasData$ = this.store.select(requestLifeCycleSelectors.selectIsRequestLifeCycleLoadedById, { requestId }); return hasData$.pipe( switchMap(hasData => { if (!hasData) { this.store.dispatch( RequestLifeCycleActions.LoadRequestLifeCycle({ action: RequestQueryActions.REQUESTS, requestId, }) ); // 等待该ID的数据加载完成 return this.store.select(requestLifeCycleSelectors.selectIsRequestLifeCycleLoadedById, { requestId }).pipe( filter(loaded => loaded), take(1) ); } return of(true); }) ); }
2. 确保组件订阅随路由参数变化更新
组件通过路由参数获取requestId时,要在参数变化时重新订阅store,避免一直监听旧ID的数据:
@Component({...}) export class RequestDetailComponent implements OnInit, OnDestroy { private destroy$ = new Subject<void>(); request$!: Observable<RequestLifeCycle | undefined>; constructor( private route: ActivatedRoute, private store: Store ) {} ngOnInit(): void { // 用async pipe自动处理订阅(推荐) this.request$ = this.route.paramMap.pipe( switchMap(params => { const requestId = params.get('requestId')!; this.store.dispatch(RequestLifeCycleActions.LoadRequestLifeCycle({ action: RequestQueryActions.REQUESTS, requestId })); return this.store.select(requestLifeCycleSelectors.selectRequestLifeCycleById, { requestId }); }) ); } ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } }
模板中使用async pipe:
<div *ngIf="request$ | async as request"> <!-- 渲染详情内容 --> </div>
3. 离开页面时重置对应状态
离开详情页时,重置当前Request ID对应的状态,避免旧数据干扰下次进入:
// 定义重置动作 export const ResetRequestLifeCycle = createAction( '[RequestLifeCycle] Reset Request Life Cycle', props<{ requestId: string }>() ); // 在reducer中处理重置逻辑 const requestLifeCycleReducer = createReducer( initialState, // ...其他动作处理 on(RequestLifeCycleActions.ResetRequestLifeCycle, (state, { requestId }) => { return { ...state, entities: { ...state.entities, [requestId]: undefined } }; }) );
在组件销毁时分发重置动作:
ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); const requestId = this.route.snapshot.paramMap.get('requestId')!; this.store.dispatch(RequestLifeCycleActions.ResetRequestLifeCycle({ requestId })); }
4. 排查订阅泄漏问题
"Cannot read properties of null (reading 'destroyed')"通常是组件已销毁但订阅仍在更新视图。确保所有手动订阅都通过takeUntil(this.destroy$)清理,优先使用async pipe(Angular会自动在组件销毁时取消订阅)。
内容的提问来源于stack exchange,提问作者Wahib Kerkeni
相关产品推荐
相关产品推荐

