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

Angular NgRx Store问题:dispatch无法更新状态及获取新闻列表

NgRx状态更新与Selector使用问题

我会基础使用Angular NgRx Store,能编写简单的reducer和action文件。现在想分离selector文件并使用createSelector()方法,但遇到两个问题:

  • 调用dispatch()触发action后,虽生成新状态,但状态值未更新;
  • 调用loadSection方法后,无法从store状态中获取新闻列表。

代码示例

news.actions.ts

import { Injectable } from '@angular/core';
import { Action } from '@ngrx/store';
import { News } from '../../model/news';

@Injectable()
export class NewsActions {
  static LOAD_SECTION_NEWS = '[News] LOAD_SECTION_NEWS';
  static FILTER_SUBSECTION = '[News] FILTER_SUBSECTION';

  LoadSectionNews(list: News[]): Action {
    return {
      type: 'LOAD_SECTION_NEWS',
      payload: list,
    };
  }
  FilterSubsection(subsection: string): Action {
    return {
      type: 'FILTER_SUBSECTION',
      payload: subsection,
    };
  }
}

news.reducer.ts

import { NewsActions } from './../actions/news.actions';
import { Action } from '@ngrx/store';
import { News } from '../../model/news';

export const LOAD_SECTION_NEWS = '[News] LOAD_SECTION_NEWS';
export const FILTER_SUBSECTION = '[News] FILTER_SUBSECTION';

export interface NewsState {
  newsList: News[];
  filter: string;
}
 
const mockNewsList = [];
export const initialState: NewsState = {
  newsList: mockNewsList,
  filter: '',
};

export function news(state = initialState, action: Action) {
  switch (action.type) {
    case LOAD_SECTION_NEWS: {
      return {
        ...state,
        newsList: this.getNewsList(action.payload),
        filter: action.payload,
      };
    }
    case FILTER_SUBSECTION: {
      return {
        ...state,
        newsList: this.getFilter(),
        filter: action.payload,
      };
    }
    default:
      return state;
  }
}

export const getNewsList = (state: any) => {
  console.log('action.payload:::', state.payload);
  return NewsState(state, { newsList: state.payload });
};

export const getFilter = (state: any) => {
  return state.filter;
};

sections.reducer.ts

import { Action } from '@ngrx/store';

export const LOAD_SECTIONS = '[Section] LOAD_SECTIONS';

export const initialState = [
  'home', 'opinion', 'world', 'national', 'politics', 'business',
  'technology', 'science', 'health', 'sports', 'arts', 'books',
  'movies', 'theater', 'fashion', 'food', 'travel', 'magazine',
  'realestate', 'automobiles',
];

export function sections(state = initialState, action: Action) {
  switch (action.type) {
    case LOAD_SECTIONS: {
      return [...state, action.payload];
    }
    default:
      return state;
  }
}

selector.ts

import { createSelector } from 'reselect';
import { getNewsList, getFilter } from './news.reducer';
export const getNews = createSelector(getNewsList, (state) => state.news);

触发Action的组件代码

loadSection(item: string) { 
  this.store.dispatch(new NewsActions().FilterSubsection(item)); 
  this.store.select(getNews).subscribe((dt) => { 
    console.log('here', dt); 
  }); 
}  

问题修复方案

1. 统一Action类型常量

news.actions.ts中定义的静态常量和返回的action type字符串不匹配,导致reducer无法识别action:

// 修复后news.actions.ts的Action返回逻辑
LoadSectionNews(list: News[]): Action {
  return {
    type: NewsActions.LOAD_SECTION_NEWS, // 使用静态常量而非硬编码字符串
    payload: list,
  };
}
FilterSubsection(subsection: string): Action {
  return {
    type: NewsActions.FILTER_SUBSECTION, // 使用静态常量而非硬编码字符串
    payload: subsection,
  };
}

2. 修正Reducer逻辑错误

  • reducer函数中this指向undefined,不能调用this.getNewsList;
  • getNewsList试图用接口构造对象,属于语法错误;
  • FILTER_SUBSECTION分支错误地将filter字符串赋值给newsList数组。

修复后的news.reducer.ts核心逻辑:

export function news(state = initialState, action: Action) {
  switch (action.type) {
    case LOAD_SECTION_NEWS: {
      return {
        ...state,
        newsList: action.payload, // 直接使用传入的新闻列表
        filter: '',
      };
    }
    case FILTER_SUBSECTION: {
      // 根据filter过滤现有新闻列表(需匹配News模型的属性)
      const filteredList = state.newsList.filter(news => news.subsection === action.payload);
      return {
        ...state,
        newsList: filteredList,
        filter: action.payload,
      };
    }
    default:
      return state;
  }
}

// 定义基础Selector,用于获取整个NewsState
export const selectNewsState = (state: { news: NewsState }) => state.news;
export const selectNewsList = (state: { news: NewsState }) => state.news.newsList;
export const selectNewsFilter = (state: { news: NewsState }) => state.news.filter;

3. 正确实现Selector

使用reselect的createSelector需要基于基础Selector派生数据,修复后的selector.ts:

import { createSelector } from 'reselect';
import { selectNewsList, selectNewsFilter } from './news.reducer';

// 带缓存的Selector:获取过滤后的新闻列表
export const selectFilteredNewsList = createSelector(
  selectNewsList,
  selectNewsFilter,
  (newsList, filter) => filter ? newsList.filter(n => n.subsection === filter) : newsList
);

4. 组件调用优化

  • 注入NewsActions而非每次创建实例;
  • 及时取消订阅避免内存泄漏:
import { Component, OnDestroy } from '@angular/core';
import { Store } from '@ngrx/store';
import { NewsActions } from './path-to/news.actions';
import { selectFilteredNewsList } from './path-to/selector';
import { Subscription } from 'rxjs';

@Component({/* 组件元数据 */})
export class YourComponent implements OnDestroy {
  private newsSub: Subscription;

  constructor(private store: Store, private newsActions: NewsActions) {}

  loadSection(item: string) {
    this.store.dispatch(this.newsActions.FilterSubsection(item));
    this.newsSub = this.store.select(selectFilteredNewsList).subscribe(list => {
      console.log('新闻列表:', list);
    });
  }

  ngOnDestroy() {
    this.newsSub?.unsubscribe();
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 17:40:57