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

React大型应用中文档列表筛选性能优化方案咨询

React大型文档列表筛选的性能优化问题

我正在开发一个大型React应用,其中某页面展示文档列表。目前用状态变量实现筛选功能,但担心现有方案存在卡顿、列表更新延迟的问题。

现有状态定义

从数据库获取的文档列表存储在状态中:

const [ documents, setDocuments ] = useState<Array<Document>>([]);

筛选条件对应的状态:

const [ createdBy, setCreatedBy ] = useState<string>('Any');
const [ search, setSearch ] = useState<string>('');
const [ open, setOpen ] = useState<Array<string>>([]);
const [ onlyCert, setOnlyCert ] = useState<number>(0);
const [ modifiedFilter, setModifiedFilter ] = useState<number>(0);
const [ farFromAverageFilter, setFarFromAverageFilter ] = useState<boolean>(false);
const [ draftFilter, setDraftFilter ] = useState<boolean>(false);
const [ dupeFilter, setDupeFilter ] = useState<boolean>(false);
const [ sourceFilter, setSourceFilter ] = useState<string>('Any');
const [ ageFilter, setAgeFilter ] = useState<[number, number]>([0,0]);

const [ filteredDocs, setFilteredDocs ] = useState([]);

筛选逻辑实现

通过useEffect钩子将筛选结果存入filteredDocs:

useEffect(() => {
  setFilteredDocs((documents
    ?.filter(e => (
      search === '' ? e : `${e.data.vehicle.v_stock_no}${e.data.vehicle.v_make}${e.data.vehicle.v_model}${e.data.vehicle.v_vin_no}${e.data.vehicle.company_name}`.toLowerCase().includes(search.toLowerCase().replace(/\s/g , '')))
    && (createdBy === 'Any' ? e : e.metadata.created_by_user_id === createdBy)
    && (onlyCert % 3 === 0 ? e : onlyCert % 3 === 1 ? e.data.vehicle.v_is_certified : !e.data.vehicle.v_is_certified)
    && (modifiedFilter % 3 === 0 ? e : modifiedFilter % 3 === 1 ? e.metadata.created_at === e.metadata.updated_at : e.metadata.created_at !== e.metadata.updated_at)
    && (farFromAverageFilter ? e.risky.length > 0 : e)
    && (sourceFilter === 'Any' ? e : (e?.data?.vehicle?.v_source?.toUpperCase() || '') === sourceFilter.toUpperCase())
    && (dupeFilter ? (documents.filter(x => e.data.vehicle.v_stock_no === x.data.vehicle.v_stock_no).length > 1 && documents.filter(x => e.data.vehicle.v_stock_no === x.data.vehicle.v_stock_no).every(e => !e.rollback))
        ||
            (documents.filter(x => e.data.vehicle.v_vin_no === x.data.vehicle.v_vin_no).length > 1 && documents.filter(x => e.data.vehicle.v_vin_no === x.data.vehicle.v_vin_no).every(e => !e.rollback)) : e)
    && (draftFilter ? e.draft : e)
    && (Math.abs(e.data.vehicle.v_days) >= ageFilter[0] && Math.abs(e.data.vehicle.v_days) <= ageFilter[1])
    )))
}, [
    documents,
    search,
    createdBy,
    onlyCert,
    modifiedFilter,
    farFromAverageFilter,
    sourceFilter,
    dupeFilter,
    draftFilter,
    ageFilter
])

当前困境

最初直接在渲染列表的map方法里应用筛选,导致每次渲染都重复执行筛选逻辑,后来改成用useEffect缓存结果,但还是希望找到更高效的优化方案,要求无需刷新页面、不用URL查询参数。


性能优化方案

1. 用useMemo替代useEffect缓存筛选结果

useMemo会在依赖变化时重新计算并直接返回结果,避免额外的状态更新(setFilteredDocs会触发组件重渲染),减少不必要的渲染次数:

const filteredDocs = useMemo(() => {
  if (!documents) return [];
  
  // 提前处理搜索关键词,避免每次filter重复计算
  const processedSearch = search.toLowerCase().replace(/\s/g, '');
  
  return documents.filter(e => {
    // 提前拼接搜索目标字符串并转小写
    const searchTarget = `${e.data.vehicle.v_stock_no}${e.data.vehicle.v_make}${e.data.vehicle.v_model}${e.data.vehicle.v_vin_no}${e.data.vehicle.company_name}`.toLowerCase();
    const matchesSearch = processedSearch === '' || searchTarget.includes(processedSearch);
    if (!matchesSearch) return false;

    // 创建者筛选
    const matchesCreatedBy = createdBy === 'Any' || e.metadata.created_by_user_id === createdBy;
    if (!matchesCreatedBy) return false;

    // 认证状态筛选
    let matchesCert = true;
    if (onlyCert % 3 !== 0) {
      matchesCert = onlyCert % 3 === 1 ? e.data.vehicle.v_is_certified : !e.data.vehicle.v_is_certified;
    }
    if (!matchesCert) return false;

    // 修改状态筛选
    let matchesModified = true;
    if (modifiedFilter % 3 !== 0) {
      matchesModified = modifiedFilter % 3 === 1 ? e.metadata.created_at === e.metadata.updated_at : e.metadata.created_at !== e.metadata.updated_at;
    }
    if (!matchesModified) return false;

    // 风险项筛选
    if (farFromAverageFilter && e.risky.length === 0) return false;

    // 来源筛选
    const matchesSource = sourceFilter === 'Any' || (e?.data?.vehicle?.v_source?.toUpperCase() || '') === sourceFilter.toUpperCase();
    if (!matchesSource) return false;

    // 草稿筛选
    if (draftFilter && !e.draft) return false;

    // 天数筛选
    const days = Math.abs(e.data.vehicle.v_days);
    return days >= ageFilter[0] && days <= ageFilter[1];
  });
}, [documents, search, createdBy, onlyCert, modifiedFilter, farFromAverageFilter, sourceFilter, draftFilter, ageFilter]);

2. 预计算重复项集合

把重复项的计算单独抽出来用useMemo缓存,避免每次筛选都遍历整个文档列表:

const duplicateStockVins = useMemo(() => {
  const stockNoCounts = new Map();
  const vinNoCounts = new Map();
  const nonRollbackDocs = documents.filter(doc => !doc.rollback);
  
  nonRollbackDocs.forEach(doc => {
    const stockNo = doc.data.vehicle.v_stock_no;
    stockNoCounts.set(stockNo, (stockNoCounts.get(stockNo) || 0) + 1);
    
    const vinNo = doc.data.vehicle.v_vin_no;
    vinNoCounts.set(vinNo, (vinNoCounts.get(vinNo) || 0) + 1);
  });
  
  return {
    duplicateStockNos: new Set([...stockNoCounts.entries()].filter(([_, count]) => count > 1).map(([stockNo]) => stockNo)),
    duplicateVinNos: new Set([...vinNoCounts.entries()].filter(([_, count]) => count > 1).map(([vinNo]) => vinNo))
  };
}, [documents]);

然后在筛选逻辑里直接用集合判断:

// 重复项筛选
if (dupeFilter) {
  const matchesDupe = duplicateStockVins.duplicateStockNos.has(e.data.vehicle.v_stock_no) || duplicateStockVins.duplicateVinNos.has(e.data.vehicle.v_vin_no);
  if (!matchesDupe) return false;
}

3. 提前处理固定筛选逻辑

对onlyCert、modifiedFilter这类数值型筛选条件,提前计算出对应的判断规则,减少筛选时的分支重复计算:

const [certFilterRule] = useMemo(() => {
  switch(onlyCert % 3) {
    case 1: return (doc) => doc.data.vehicle.v_is_certified;
    case 2: return (doc) => !doc.data.vehicle.v_is_certified;
    default: return () => true;
  }
}, [onlyCert]);

筛选时直接调用certFilterRule(e)即可。

4. 列表虚拟化

如果文档数量极大(上万条),用react-window或react-virtualized实现列表虚拟化,只渲染当前视口内的文档项,大幅减少DOM节点数量,提升渲染性能。

5. 后端分页+筛选

若前端处理数据量过大,建议后端支持分页和筛选逻辑,前端只请求当前页的筛选结果,减少前端需要处理的数据量。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 06:13:10