如何解决React Recharts 渲染了大量数据的阻塞
我有一个 Recharts 用例,我渲染了超过 20,000 个数据点,这导致了阻塞渲染:
(CodeSandBox 有一个小的脉冲动画,因此在创建新图表数据时更容易看到阻塞渲染。)
使用开发工具衡量性能时,很明显,造成这种情况的原因不是浏览器的 painting
或 rendering
活动,而是 Recharts 的 scripting
活动:
我绝对不想在这里责怪 Recharts,20k 点很多,但有没有人知道是否有办法解决阻塞渲染?
我尝试过的事情:
1.) 增量加载
增量加载更多数据(例如 2k + 2k + 2k + ... = 20k),这只会导致更多、更小的渲染阻塞时刻。
2.) 渲染前加载动画
在渲染组件中添加了一个小的布尔状态来跟踪“挂载”状态,当图表组件挂载时,它至少会显示一个加载动画,因此用户不会等待空白页面/路由切换:>
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>
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;
},[data.areas]
)}
</p>
<Animation />
<Chart data={data} />
</>
)}
</div>
);
}
在这个例子中,我使用了新的unstable_useTransition 钩子,并在点击按钮时使用 startTransition,用于图表数据的无阻塞计算。
动画不是完美的 60fps,但网站仍然响应!
查看此代码分支中的差异:
https://codesandbox.io/s/concurrent-mode-recharts-render-blocking-forked-m62kf?file=/src/App.tsx
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。