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

Redux Toolkit实现商品筛选排序如何精简冗余useEffect

商品筛选页冗余useEffect优化方案

原实现核心问题

当前代码的核心问题是把派生数据当成独立状态维护:筛选排序后的商品列表完全是基于原始商品数据、Redux筛选参数计算出来的派生值,不需要用useState存储,更不需要多个useEffect串行更新。
原有写法的明显缺陷:

  • 多个useEffect按顺序触发状态更新,会导致组件多次重渲染,性能损耗大
  • 每个effect的依赖数组需要重复添加大量依赖,很容易出现漏写、错写,导致筛选逻辑不触发或意外触发
  • 串行执行筛选/排序逻辑容易出现覆盖问题,比如清空某一个筛选条件时,之前effect执行的结果会残留,导致筛选结果错误
  • 原有多选匹配逻辑有bug:直接展开size/brand数组做判断,无法支持多个尺寸、多个品牌同时选中的筛选场景

最优实现:用useMemo计算派生值,完全移除冗余useEffect

所有筛选、排序逻辑可以统一收敛到useMemo中,不需要维护单独的filteredProducts状态,也不需要任何相关的useEffect。

第一步:获取源数据

直接从Redux和useQuery拿到最原始的数据源,不做中间状态存储:

const { size, brand, price, sort } = useSelector((state) => state.filter);
const { loading, error, data } = useQuery(GET_PRODUCTS);
// 给原始商品数据加空数组兜底,避免接口返回空时报错
const products = data?.getProducts || [];

第二步:统一计算筛选+排序结果

遵循先筛选、后排序的顺序(减少排序需要处理的数据量,性能更好),在useMemo中完成所有计算:

const filteredProducts = useMemo(() => {
  // 第一步:遍历原始商品,匹配所有筛选条件
  let result = products.filter((product) => {
    // 尺寸匹配:未选尺寸默认返回全部商品,选中尺寸则匹配任意一个选中值
    const sizeMatched = size.length === 0 
      ? true 
      : size.some((selectedSize) => product.size.includes(Number(selectedSize)));
    
    // 品牌匹配:逻辑同尺寸
    const brandMatched = brand.length === 0
      ? true
      : brand.some((selectedBrand) => product.brand.includes(selectedBrand));
    
    // 价格区间匹配:未选区间默认返回全部,选中则判断价格在区间内(包含边界值)
    const priceMatched = price.length === 0
      ? true
      : product.price >= price[0][0] && product.price <= price[0][1];

    return sizeMatched && brandMatched && priceMatched;
  });

  // 第二步:对筛选后的结果做排序
  if (sort.length > 0) {
    // 注意先拷贝数组再排序,Array.sort是原地修改,会污染原始商品数据
    result = result.slice().sort((a, b) => {
      return sort.includes('price-highest') 
        ? b.price - a.price 
        : a.price - b.price;
    });
  }

  return result;
// 依赖只需要包含所有用到的源数据即可,逻辑清晰不会漏写
}, [products, size, brand, price, sort]);

Redux中存储的筛选状态结构参考:
Redux筛选状态结构示例

方案优势

  • 代码量缩减60%以上,没有任何冗余的状态更新逻辑,所有计算规则集中在一处,可读性极强
  • 不会触发多次重渲染,所有计算在单次渲染周期内完成,性能更好
  • 不存在依赖数组配置错误问题:只要原始商品数据、任意筛选参数变化,会自动重新计算最终结果,清空筛选条件时也会自动回到全量商品列表,不会残留旧筛选结果
  • 修复了原逻辑中多选筛选不生效、价格边界值被过滤、sort原地修改原数组的隐蔽bug

扩展优化(复杂筛选场景适用)

如果后续要增加更多筛选维度,可以把每个筛选规则抽成独立纯函数,进一步降低维护成本:

// 抽离独立筛选规则
const filterChecks = {
  matchSize: (product, selectedSizes) => selectedSizes.length === 0 || selectedSizes.some(s => product.size.includes(Number(s))),
  matchBrand: (product, selectedBrands) => selectedBrands.length === 0 || selectedBrands.some(b => product.brand.includes(b)),
  matchPrice: (product, priceRange) => priceRange.length === 0 || (product.price >= priceRange[0][0] && product.price <= priceRange[0][1])
}

const filteredProducts = useMemo(() => {
  let result = products.filter(product => 
    filterChecks.matchSize(product, size)
    && filterChecks.matchBrand(product, brand)
    && filterChecks.matchPrice(product, price)
  );

  if (sort.length > 0) {
    result = result.slice().sort((a, b) => sort.includes('price-highest') ? b.price - a.price : a.price - b.price);
  }
  return result;
}, [products, size, brand, price, sort]);

内容的提问来源于stack exchange,提问作者עמית שוקרון

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:39:15