React Hooks组件在Accordion展开后重渲染获取正确offsetWidth方案求助
可行解决方案
方案1:使用双层requestAnimationFrame
利用requestAnimationFrame的执行时序特性,两层嵌套可以保证回调在浏览器完成当前帧的布局、绘制之后执行,此时就能读取到正确的offsetWidth值:
import { useEffect, useRef, useState } from 'react'; const InnerComponent = ({ isAccordionOpen }) => { const targetElRef = useRef(null); const [contentWidth, setContentWidth] = useState(0); useEffect(() => { // 手风琴关闭时跳过逻辑 if (!isAccordionOpen) return; // 第一层raf等待DOM更新,第二层等待浏览器绘制完成 requestAnimationFrame(() => { requestAnimationFrame(() => { if (targetElRef.current) { setContentWidth(targetElRef.current.offsetWidth); } }); }); }, [isAccordionOpen]); return <div ref={targetElRef}>你的组件内容</div>; };
方案2:使用ResizeObserver(推荐)
ResizeObserver是浏览器原生提供的元素尺寸监听API,会在目标元素尺寸发生变化时触发回调,不受组件渲染时机、元素显隐状态影响,不需要依赖外部传入的手风琴开闭状态,是稳定性最高的方案:
import { useEffect, useRef, useState } from 'react'; const InnerComponent = () => { const targetElRef = useRef(null); const [contentWidth, setContentWidth] = useState(0); useEffect(() => { const resizeObserver = new ResizeObserver((entries) => { for (const entry of entries) { const currentWidth = entry.contentRect.width; // 过滤掉元素不可见时的0值无效更新 if (currentWidth > 0) { setContentWidth(currentWidth); } } }); if (targetElRef.current) { resizeObserver.observe(targetElRef.current); } // 组件卸载时销毁监听器避免内存泄漏 return () => resizeObserver.disconnect(); }, []); return <div ref={targetElRef}>你的组件内容</div>; };
方案3:使用setTimeout 0延时兜底
把读取尺寸的逻辑放到宏任务队列执行,会等待当前的DOM更新、浏览器绘制完成后再执行,写法最简单,适合轻量场景:
useEffect(() => { if (!isAccordionOpen) return; setTimeout(() => { if (targetElRef.current) { setContentWidth(targetElRef.current.offsetWidth); } }, 0); }, [isAccordionOpen]);
方案对比
- 双层
requestAnimationFrame:轻量无额外依赖,时序可控,适合手风琴无展开动画的场景 ResizeObserver:兼容性覆盖所有现代浏览器,自动监听所有尺寸变化场景,不需要绑定手风琴状态,为最优方案setTimeout:写法最简单,但是时序优先级最低,极端场景下可能出现延迟,适合临时测试、简单场景使用
内容的提问来源于stack exchange,提问作者edo
相关产品推荐
相关产品推荐

