使用@ngrx/store添加对象到状态数组后,组件无法渲染更新内容的问题
Hey there! Let's dig into why your @ngrx/store notifications aren't showing up in the template. This is a super common gotcha with NgRx, usually tied to immutability or subscription missteps. Let's break it down step by step:
NgRx relies entirely on immutable state updates—if you mutate the existing state array instead of returning a brand new one, Angular's change detection won't pick up the change, which is why your *ngFor doesn't re-run and length stays the same.
❌ Wrong Way (Mutating the Existing Array)
// notification.reducer.ts case NotificationActions.AddNotification: // This mutates the original state array—NgRx won't detect this change state.notifications.push(action.payload); return state;
✅ Correct Way (Return New State with New Array)
// notification.reducer.ts case NotificationActions.AddNotification: // Spread the existing state and create a new array with the new item return { ...state, notifications: [...state.notifications, action.payload] };
The spread operators (...) ensure we're creating new objects/arrays instead of modifying the original state reference.
Make sure your component is actually listening to the store's state changes. The async pipe is the recommended approach (it handles subscriptions and cleanup automatically):
Component Code
// notification-show.component.ts import { Store } from '@ngrx/store'; import { select } from '@ngrx/store'; import { AppState } from '../state/app.state'; import { selectAllNotifications } from '../state/notification.selectors'; // Define an observable for the notifications notifications$ = this.store.pipe(select(selectAllNotifications)); constructor(private store: Store<AppState>) {}
Template Code
<!-- notification-show.component.html --> <!-- Use async pipe to subscribe to the observable --> <div *ngFor="let notification of notifications$ | async"> {{ notification.message }} <!-- Replace with your notification properties --> </div> <!-- Check length with the async pipe too --> <p>Total notifications: {{ (notifications$ | async)?.length }}</p>
If you're using a manual subscription, don't forget to clean it up to avoid memory leaks:
// notification-show.component.ts import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; private destroy$ = new Subject<void>(); notifications: Notification[] = []; ngOnInit() { this.store.pipe( select(selectAllNotifications), takeUntil(this.destroy$) // Unsubscribe when component is destroyed ).subscribe(notifications => { this.notifications = notifications; }); } ngOnDestroy() { this.destroy$.next(); this.destroy$.complete(); }
Ensure your selectors are pointing to the correct slice of your app state:
App State Interface
// app.state.ts export interface AppState { notifications: NotificationState; // Match the key used when registering your reducer } export interface NotificationState { notifications: Notification[]; // The array holding your notifications }
Selector Definition
// notification.selectors.ts import { createFeatureSelector, createSelector } from '@ngrx/store'; import { NotificationState } from './notification.state'; // Get the root notification state slice const selectNotificationState = createFeatureSelector<NotificationState>('notifications'); // Select the actual notifications array export const selectAllNotifications = createSelector( selectNotificationState, (state: NotificationState) => state.notifications );
Reducer Registration
Confirm your reducer is registered under the correct key in your store module:
// app.module.ts import { StoreModule } from '@ngrx/store'; import { notificationReducer } from './state/notification.reducer'; @NgModule({ imports: [ StoreModule.forRoot({ notifications: notificationReducer // Key matches the feature selector name }) ] }) export class AppModule {}
Make sure you're sending the right payload when dispatching the add action:
Action Definition
// notification.actions.ts import { createAction, props } from '@ngrx/store'; import { Notification } from './notification.model'; export const addNotification = createAction( '[Notification] Add Notification', props<{ notification: Notification }>() // Payload structure matches what you'll dispatch );
Dispatching the Action
// In any component/service where you add notifications import { Store } from '@ngrx/store'; import { addNotification } from '../state/notification.actions'; constructor(private store: Store<AppState>) {} addNewNotification() { const newNotification: Notification = { id: Date.now(), message: 'This is a new notification!' }; // Dispatch with the correct payload structure this.store.dispatch(addNotification({ notification: newNotification })); }
- Add
console.login your reducer to confirm the action is being caught and the new state is returned correctly. - Use the
tapoperator in your component to log the notifications stream:notifications$ = this.store.pipe( select(selectAllNotifications), tap(notifications => console.log('Current notifications:', notifications)) ); - Use Redux DevTools to inspect state changes—this lets you visually confirm if the state updates when you dispatch the action.
内容的提问来源于stack exchange,提问作者Developer Team

