如何基于filterState变化按需过滤数组,避免重复全量过滤?
问题描述
我有一个名为filterState的过滤状态,会根据过滤操作更新,结构如下:
filterState = {"resolved": true, type: "someString", "isTicketRequired": false}
需要用它过滤名为data的数组,data结构示例:
data = [ {...some properties, resolve: true, type: "minor", isTicketRequired: false}, {...some properties, resolve: false, type: "major", isTicketRequired: true}, {...some properties, resolve: true, type: "medium", isTicketRequired: false} ]
当前使用的过滤代码如下:
const filterArray = Object.entries(filterState); let result = []; for (let i = 0; i < filterArray.length; i++) { result = data.filter( (d) => d[filterArray[i][0]] === filterArray[i][1] ); } return result;
这段代码能正常运行,但每次filterState更新都要重新执行全量循环。我想存储之前的过滤结果,当filterState更新时仅按需处理——比如resolved从true变为false时,只基于之前的结果重新过滤resolved项,无需执行全量循环。我认为可以用useEffect实现,但缺乏具体思路。
优化方案
核心思路是缓存每一步的过滤结果,仅针对变化的过滤条件更新数据,结合React的useState缓存中间结果,useEffect监听过滤状态的变化触发对应更新。
1. 缓存原始数据与过滤结果
用useRef保存原始数据(避免组件重渲染时丢失),useState维护当前过滤结果和上一次的过滤状态,用于对比变化:
import { useState, useEffect, useRef } from 'react'; function FilterComponent({ data }) { // 缓存原始数据,避免每次渲染重新获取 const originalDataRef = useRef(data); // 当前最终过滤结果 const [filteredData, setFilteredData] = useState(data); // 保存上一次的过滤状态,用于对比变化 const [prevFilterState, setPrevFilterState] = useState({}); // 当前过滤状态(可从props传入或内部维护) const [filterState, setFilterState] = useState({ resolved: true, type: "someString", isTicketRequired: false });
2. 仅处理变化的过滤条件
在useEffect中对比新旧过滤状态,找出变化的字段,只对这些字段执行过滤:
useEffect(() => { // 筛选出本次更新中变化的过滤字段 const changedFilters = Object.entries(filterState).filter(([key, value]) => { return prevFilterState[key] !== value; }); if (changedFilters.length === 0) return; // 确定过滤数据源:首次过滤用原始数据,后续用上一次的过滤结果 let sourceData = Object.keys(prevFilterState).length === 0 ? originalDataRef.current : filteredData; // 仅对变化的字段执行过滤 let updatedData = sourceData; changedFilters.forEach(([key, value]) => { // 注意:你原数据中字段是`resolve`,但过滤状态是`resolved`,这里要做字段映射 const dataKey = key === 'resolved' ? 'resolve' : key; updatedData = updatedData.filter(item => item[dataKey] === value); }); // 更新结果与上一次的过滤状态 setFilteredData(updatedData); setPrevFilterState({...filterState}); }, [filterState, filteredData, prevFilterState]);
3. 简化版:分字段监听
如果过滤条件数量不多,也可以针对每个字段单独设置useEffect监听,逻辑更直观:
// 仅监听resolved的变化 useEffect(() => { setFilteredData(prev => prev.filter(item => item.resolve === filterState.resolved)); }, [filterState.resolved]); // 仅监听type的变化 useEffect(() => { setFilteredData(prev => prev.filter(item => item.type === filterState.type)); }, [filterState.type]); // 仅监听isTicketRequired的变化 useEffect(() => { setFilteredData(prev => prev.filter(item => item.isTicketRequired === filterState.isTicketRequired)); }, [filterState.isTicketRequired]);
注意:首次加载时要确保所有初始过滤条件都生效,可在组件挂载时先执行一次全量过滤。
关键注意点
- 字段名统一:确保过滤状态的key与数据中的字段名匹配,或提前做好映射;
- 重置逻辑:若需要清空所有过滤条件,直接将
filteredData设回原始数据,prevFilterState设为空对象; - 性能优化:如果数据量极大,可考虑结合
useMemo进一步缓存过滤逻辑。
内容的提问来源于stack exchange,提问作者RH-st
相关产品推荐
相关产品推荐

