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

