如何解决Ngrx reducer中用filter+includes过滤后状态永久变更的问题
问题根源
你当前的写法直接修改了存储原始歌曲列表的状态,每次执行过滤操作都会将状态覆盖为过滤后的子集,原始未匹配数据会被永久删除,自然无法在输入框清空后恢复全量数据。reducer只应该用来存储基础状态,过滤后的列表属于派生状态,不应该在reducer中直接修改原始数据生成。
解决方案
步骤1:调整State结构,拆分基础状态
你需要在状态中同时留存全量原始数据、当前过滤参数,永远不要修改全量原始数据集:
// 首先定义状态类型 export interface DataState { allSongs: Songs[]; // 永久存储全量原始歌曲数据,过滤操作不会修改该字段 filterText: string; // 存储当前输入的过滤关键词 filterField: string; // 存储当前选中的过滤字段,对应action传入的field参数 } // 初始状态配置,allSongs赋值为你的全量初始歌曲数据 const initialState: DataState = { allSongs: [/* 你的原始全量歌曲数据 */], filterText: '', filterField: 'artist' };
步骤2:修改Reducer逻辑,仅更新过滤参数
reducer不再执行过滤操作,只负责更新过滤相关的参数,保留全量原始数据:
export const dataReducer = createReducer<DataState>( initialState, on(filterTable, (state, action) => { return { ...state, filterText: action.text, filterField: action.field }; }) );
步骤3:使用Ngrx Selector生成实时过滤的派生状态
通过Selector基于基础状态实时计算过滤结果,不会修改原始数据,且会随参数变化自动更新:
import { createSelector } from '@ngrx/store'; // 先取到当前模块的状态 const selectDataState = (state: RootState) => state.data; // 这里的RootState替换为你项目的根状态类型 // 导出过滤后的歌曲列表选择器,组件直接订阅该选择器即可 export const selectFilteredSongs = createSelector( selectDataState, ({ allSongs, filterText, filterField }) => { // 过滤关键词为空时直接返回全量数据 if (!filterText?.trim()) return allSongs; const lowerFilterText = filterText.toLowerCase(); // 基于当前过滤参数实时计算结果 return allSongs.filter(item => { const fieldValue = item[filterField as keyof Songs]?.toString().toLowerCase() || ''; return fieldValue.includes(lowerFilterText); }); } );
步骤4:组件中使用
输入框每次输入时只需要触发filterTable action传入最新的输入内容和过滤字段,组件直接订阅selectFilteredSongs获取实时的过滤结果即可,无需手动处理重置逻辑,关键词为空时自动返回全量数据。
内容的提问来源于stack exchange,提问作者artworkjpm
相关产品推荐
相关产品推荐

