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

Redux Toolkit(TypeScript)中数组筛选排序搜索的正确选择器实现

你的Reselect选择器实现问题及修正方案

你当前的实现存在记忆化失效的问题:每次调用selectBy(filterBy, sorting, search)都会创建一个全新的createSelector实例,导致Reselect无法复用之前的计算缓存——组件每次渲染都会触发重新计算,和直接在组件里写逻辑没区别。

另外还有一个潜在问题:原代码中items.sort()会直接修改Redux Store里的原数组,违反了Redux状态不可变的原则,可能引发意外的状态变更。

正确实现分两种场景:


场景1:筛选/排序/搜索参数存在Redux Store中

如果filterBy、sorting、search已经存在Redux切片里,直接把它们作为选择器的输入依赖,创建单一的记忆化选择器:

// 在slice文件中
const selectItems = (state: RootState) => state.product.items;
const selectFilterBy = (state: RootState) => state.product.filterBy;
const selectSorting = (state: RootState) => state.product.sorting;
const selectSearch = (state: RootState) => state.product.search;

export const selectFilteredSortedItems = createSelector(
  [selectItems, selectFilterBy, selectSorting, selectSearch],
  (items, filterBy, sorting, search) => {
    // 先复制原数组,避免修改Redux中的原始状态
    const lowerSearch = search.toLowerCase();
    return [...items]
      // 先筛选分类(减少后续排序的数组长度,提升性能)
      .filter(item => filterBy !== "all" ? item.category === filterBy : true)
      // 排序
      .sort((a, b) => b[sorting] - a[sorting])
      // 搜索筛选
      .filter(item => 
        item.title.toLowerCase().includes(lowerSearch) ||
        item.category.toLowerCase().includes(lowerSearch) ||
        item.ingredients.some(ing => ing.toLowerCase().includes(lowerSearch))
      );
  }
);

组件中直接使用:

const items = useAppSelector(selectFilteredSortedItems);

这种方式下,选择器是固定实例,Reselect会自动监听四个输入值的变化,只有当其中任一值改变时才重新计算,完全发挥记忆化的作用。


场景2:筛选/排序/搜索参数是组件本地状态

如果这些参数是组件的useState本地状态,需要用useMemo缓存selectBy返回的选择器实例,避免每次渲染都创建新的选择器:

// 组件中
const [filterBy, setFilterBy] = useState<CategoriesType>("all");
const [sorting, setSorting] = useState<SortType>("price");
const [search, setSearch] = useState("");

// 缓存选择器实例,只有当参数变化时才重新创建
const memoizedSelector = useMemo(() => {
  return selectBy(filterBy, sorting, search);
}, [filterBy, sorting, search]);

const items = useAppSelector(memoizedSelector);

同时要修改slice中的selectBy,确保排序时不修改原数组:

// slice文件中修正后的selectBy
export const selectBy = (
  filterBy: CategoriesType,
  sorting: SortType,
  search: string
) =>
  createSelector(selectItems, (items) => {
    const lowerSearch = search.toLowerCase();
    return [...items]
      .filter((item) =>
        filterBy !== "all" ? item.category === filterBy : true
      )
      .sort((a, b) => b[sorting] - a[sorting])
      .filter(
        (item) =>
          item.title.toLowerCase().includes(lowerSearch) ||
          item.category.toLowerCase().includes(lowerSearch) ||
          item.ingredients.some((ing) => ing.toLowerCase().includes(lowerSearch))
      );
  });

额外优化建议

  • 把search的小写转换提前做一次,避免在filter循环中重复执行,提升性能
  • 调整操作顺序:先做分类筛选(减少数组长度),再排序,最后做搜索筛选,比先排序再筛选更高效(因为排序的时间复杂度是O(n log n),数组越短排序越快)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 22:12:20