javascript - React Recharts 使用大量数据渲染阻塞

标签 javascript reactjs typescript performance recharts

我有一个 Recharts 用例,我在其中渲染超过 20,000 个数据点,这会导致渲染阻塞:
See on CodeSandbox
(CodeSandbox 有一个小的脉冲动画,因此在创建新图表数据时更容易看到阻塞渲染。)
使用开发工具测量性能时,很明显这不是浏览器的 paintingrendering事件,但 Recharts 的 scripting事件:
performance
我绝对不想在这里责怪 Recharts,20k 点很多,但是有没有人知道是否有办法绕过阻塞渲染?
我试过的东西:
1.) 增量加载
增量加载更多数据(例如 2k + 2k + 2k + ... = 20k),这只会导致更多、更小的渲染阻塞时刻。
2.) 渲染前加载动画
在渲染组件中添加了一个小 bool 状态来跟踪“已安装”状态,它至少会在图表组件安装时显示加载动画,因此用户无需等待空白页面/路由切换:

const [showLoading, setShowLoading] = useState<boolean>(true);
const { isLoading, data } = useXY()   // remote data fetching
const [isMounted, setIsMounted] = useState(false);

useEffect(() => {
  setIsMounted(true);
}, []);

useEffect(() => {
  setShowLoading(isLoading || !isMounted);
}, [isLoading, isMounted]);

...

if (showLoading) return <LoadingAnimation />

return <div>...chart...</div>


图表代码:
(完整代码请参见 CodeSandbox)
function Chart({ data }: { data: Data }) {
  console.log("⌛ Rendering chart");

  const lineData = useMemo(() => {
    return data.lines;
  }, [data.lines]);

  const areaData = useMemo(() => {
    return data.areas;
  }, [data.areas]);

  return (
    <ComposedChart
      width={500}
      height={400}
      margin={{
        top: 20,
        right: 20,
        bottom: 20,
        left: 20
      }}
    >
      <CartesianGrid stroke="#f5f5f5" />
      <XAxis dataKey="ts" type="number" />
      <YAxis />
      <Tooltip />
      {areaData.map((area) => (
        <Area
          // @ts-ignore
          data={area.data}
          dataKey="value"
          isAnimationActive={false}
          key={area.id}
          type="monotone"
          fill="#8884d8"
          stroke="#8884d8"
        />
      ))}
      {lineData.map((line) => (
        <Line
          data={line.data}
          dataKey="value"
          isAnimationActive={false}
          key={line.id}
          type="monotone"
          stroke="#ff7300"
        />
      ))}
    </ComposedChart>
  );
}

最佳答案

你可以使用(当前实验的)React Concurrent Mode .
在并发模式下,渲染是非阻塞的。

export default function App() {
  // imagine data coming from an async request
  const [data, setData] = useState<Data>(() => createData());
  const [startTransition, isPending] = unstable_useTransition();
  function handleNoneBlockingClick() {
    startTransition(() => setData(createData()));
  }
  function handleBlockingClick() {
    setData(createData());
  }
  return (
    <div className="App">
      <button onClick={handleNoneBlockingClick}>
        (None blocking) Regenerate data
      </button>
      <button onClick={handleBlockingClick}>(Blocking) Regenerate data</button>
      {isPending && <div>...pending</div>}
      {data && (
        <>
          <p>
            Number of data points to render:{" "}
            {useMemo(
              () =>
                data.lines.reduce((acc, item) => {
                  return acc + item.data.length;
                }, 0),
              [data.lines]
            ) +
              useMemo(
                () =>
                  data.areas.reduce((acc, item) => {
                    return acc + item.data.length;
                  }, 0),
                [data.areas]
              )}
          </p>
          <Animation />
          <Chart data={data} />
        </>
      )}
    </div>
  );
}
在这个例子中,我使用了新的unstable_useTransition 钩子(Hook),并在单击按钮时使用startTransition 来实现图表数据的无阻塞计算。
动画不是完美的 60fps,但网站仍然响应!
查看此代码分支中的差异:
https://codesandbox.io/s/concurrent-mode-recharts-render-blocking-forked-m62kf?file=/src/App.tsx

关于javascript - React Recharts 使用大量数据渲染阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66332632/

相关文章:

javascript - React 路由器匹配路径问题

javascript - react |如何从状态更新中删除 Prevent.default

javascript - Node.js 类模块系统

javascript - meteor 电流用户

php - 电子邮件验证正则表达式问题

angular - Angular 2 中的全局管道

javascript - Angular2,设置超时变量更改后 View 不更新

javascript - 从 firefox 附加组件中隐藏位置栏

javascript - 如何在不刷新chrome页面的情况下清除控制台数据

javascript - 如何删除事件监听器,以便在卸载组件时它不会尝试更新状态?