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

如何优化React大型列表渲染?已用memo/useCallback仍卡顿的解决方法

优化React大型列表渲染的方案

针对你的问题,先从修复当前重渲染问题入手,再解决大型列表的DOM性能问题,具体方案如下:

1. 修正React.memo的使用方式

你当前在map循环内每次渲染都调用React.memo,这会导致每次List组件重渲染时都生成新的memoized组件实例,完全起不到缓存作用。正确的做法是直接将Item组件本身用React.memo包裹:

// 直接包裹Item组件,而非在map循环内使用
const Item = React.memo(({ item, onUpdate }) => {
  // ... 原Item组件内容
});

2. 用useCallback缓存更新函数,避免不必要的prop变化

List组件中的handleUpdate函数每次重渲染都会重新创建,导致Item组件的onUpdate prop始终变化,即使使用了React.memo也会触发重渲染。这里可以通过useCallback缓存函数,同时改用setData的函数式更新消除对data的依赖:

import React, { useState, useCallback } from "react";

const List = ({ items }) => {
  const [data, setData] = useState(items);

  // 用useCallback缓存函数,依赖数组为空(因使用函数式更新)
  const handleUpdate = useCallback((updatedItem) => {
    setData(prevData => {
      const index = prevData.findIndex(i => i.id === updatedItem.id);
      const newData = [...prevData];
      newData[index] = updatedItem;
      return newData;
    });
  }, []);

  return (
    <div className="list">
      {data.map((item) => (
        <Item key={item.id} item={item} onUpdate={handleUpdate} />
      ))}
    </div>
  );
};

3. 优化Item组件的状态同步

你当前在Item组件内部维护了data状态,但未同步props.item的变化(比如其他操作导致列表数据更新时)。可以添加useEffect来同步props,避免数据不一致:

const Item = React.memo(({ item, onUpdate }) => {
  const [data, setData] = useState(item);

  // 当props.item变化时,同步到本地state
  useEffect(() => {
    setData(item);
  }, [item]);

  const handleChange = (e) => {
    setData(prev => ({ ...prev, value: e.target.value }));
  };

  const handleClick = () => {
    onUpdate(data);
  };

  return (
    <div className="item">
      <p>{data.name}</p>
      <input type="text" value={data.value} onChange={handleChange} />
      <button onClick={handleClick}>Update</button>
    </div>
  );
});

4. 虚拟滚动:解决1000条数据的DOM性能问题

即使解决了重渲染,1000个DOM节点同时存在于页面中依然会导致布局和渲染性能问题。虚拟滚动只渲染当前视口内的列表项,大幅减少DOM数量,是大型列表的必备优化方案。

以react-window为例,实现方式如下:

首先安装依赖:

npm install react-window

然后修改List组件:

import { FixedSizeList as List } from 'react-window';

// 定义每个列表项的高度
const ITEM_HEIGHT = 80;

const VirtualList = ({ items, handleUpdate }) => {
  const Row = ({ index, style }) => {
    const item = items[index];
    return (
      <div style={style}>
        <Item key={item.id} item={item} onUpdate={handleUpdate} />
      </div>
    );
  };

  return (
    <List
      height={600} // 列表容器高度
      itemCount={items.length}
      itemSize={ITEM_HEIGHT}
      width="100%"
    >
      {Row}
    </List>
  );
};

// 原List组件修改为使用虚拟滚动
const ListContainer = ({ items }) => {
  const [data, setData] = useState(items);
  const handleUpdate = useCallback((updatedItem) => {
    setData(prevData => {
      const index = prevData.findIndex(i => i.id === updatedItem.id);
      const newData = [...prevData];
      newData[index] = updatedItem;
      return newData;
    });
  }, []);

  return <VirtualList items={data} handleUpdate={handleUpdate} />;
};

额外优化建议

  • 精简props传递:如果Item组件不需要item的所有字段,只传递所需的id、name、value等字段,减少props对比的开销。
  • 用useMemo缓存复杂计算:如果Item组件中有复杂的计算逻辑,用useMemo缓存计算结果,避免每次渲染重复计算。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 18:40:20