React:子组件异步渲染更新DOM完成后触发指定函数
问题场景
在ParentComponent中渲染了图表组件ResponsiveLine,现有函数calculateHeight,用于计算图表部分DOM元素的高度。
为保证计算结果准确,calculateHeight函数必须在ResponsiveLine图表渲染完成后触发才能正常运行。
当前遇到问题:默认useEffect会在子组件内部渲染流程未完全结束时执行,导致无法正确获取图表DOM元素的实际尺寸。
以下是简化后的问题示例代码:
const ParentComponent = () => { const myref = useRef(null); const [marginBottom, setMarginBottom] = useState(60); useEffect(() => { setMarginBottom(calculateHeight(myref)); }); return ( <div ref={myref}> <ResponsiveLine marginBottom={marginBottom}/> </div>) }
补充说明:
ResponsiveLine为第三方库提供的子组件,无法对其源码进行修改。
解决方案
核心原因是React的useEffect默认触发时机仅保证当前组件的DOM节点已插入文档,不保障第三方子组件内部的异步绘制、尺寸计算逻辑执行完成,因此需要等浏览器实际完成渲染绘制后再执行高度计算,有两种稳定可用的实现方式:
方案1:requestAnimationFrame 等待渲染帧完成
requestAnimationFrame的回调会在浏览器下一帧绘制前执行,嵌套两层可以覆盖绝大多数第三方图表组件的同步/短异步渲染场景,实现简单、性能开销小:
const ParentComponent = () => { const myref = useRef(null); const [marginBottom, setMarginBottom] = useState(60); useEffect(() => { // 第一帧等DOM提交完成,第二帧等所有绘制逻辑落地 const rafId = requestAnimationFrame(() => { requestAnimationFrame(() => { setMarginBottom(calculateHeight(myref)); }); }); return () => cancelAnimationFrame(rafId); // 依赖项加上marginBottom,保证边距更新重渲染后能重新计算 }, [marginBottom]); return ( <div ref={myref}> <ResponsiveLine marginBottom={marginBottom}/> </div>) }
方案2:ResizeObserver 监听尺寸稳定后触发
如果图表存在加载动画、异步拉取数据后渲染的逻辑,用ResizeObserver监听容器DOM的尺寸变化是最稳妥的方案——只要图表渲染导致容器尺寸变动,就会触发计算,完全不依赖固定时序,同时还能自动适配窗口 resize、容器尺寸变化的场景:
const ParentComponent = () => { const myref = useRef(null); const [marginBottom, setMarginBottom] = useState(60); useEffect(() => { const container = myref.current; if (!container) return; const calcAndUpdate = () => { const newMarginVal = calculateHeight(myref); // 值没变化就不触发setState,避免死循环 if (newMarginVal !== marginBottom) { setMarginBottom(newMarginVal); } }; const observer = new ResizeObserver(calcAndUpdate); observer.observe(container); // 初始化兜底执行一次 calcAndUpdate(); return () => observer.disconnect(); }, [marginBottom]); return ( <div ref={myref}> <ResponsiveLine marginBottom={marginBottom}/> </div>) }
注意:不要用固定时长的
setTimeout做延迟,不同设备性能、网络状态下组件渲染完成时间不固定,固定延迟既可能出现计算时机过早的问题,也可能因为延迟过长导致页面视觉闪烁。
内容的提问来源于stack exchange,提问作者Giardiv
相关产品推荐
相关产品推荐

