React中使用Ref获取元素clientWidth/clientHeight值不一致问题
为什么解构的宽高和控制台显示的不一致?
这事儿我之前也踩过坑,主要是两个常见原因:
1. 浏览器控制台的「惰性求值」坑
浏览器控制台里的对象是实时引用,不是你console.log那一刻的快照。当你第一次打印this.ref.current时,控制台只会显示对象的引用,等你手动展开它的时候,才会去读取元素当前的最新尺寸。而你解构clientWidth和clientHeight的时机,是在componentDidMount执行的瞬间,那时候元素的尺寸可能还没最终确定,所以拿到的是当时的旧值(1920 248);等你后来展开控制台的对象时,元素已经完成布局,尺寸变成了正确的783和1385,就造成了“控制台显示和变量值不一致”的错觉。
你可以做个测试验证:把解构后的变量也塞进对象里打印,比如:
const elem = this.ref.current; const { clientWidth, clientHeight } = elem; console.log({ elem, clientWidth, clientHeight });
这时候你会看到,对象里的clientWidth和clientHeight就是1920 248,和你单独打印的数值一致——因为这次是打印的快照,不是实时引用。
2. 元素布局未完全完成
componentDidMount只是表示组件已经挂载到DOM树上,但并不代表所有样式、依赖资源(比如图片、异步加载的CSS)都已经加载完成,元素的最终布局可能还在等待这些资源。比如:
- 父元素的尺寸是由异步数据或者图片加载决定的,在
componentDidMount时父元素还没撑开; - 有CSS动画、过渡效果,元素尺寸是逐步变化的;
- 页面存在重排(reflow),
componentDidMount执行时重排还没结束。
这时候你获取的clientWidth和clientHeight是元素初始的尺寸,之后元素完成布局才变成正确值,而控制台的对象引用会实时反映这个变化。
怎么解决?
如果要获取元素的最终尺寸,可以试试这些方法:
- 使用
requestAnimationFrame延迟获取,等下一次浏览器重绘时再读取:componentDidMount() { requestAnimationFrame(() => { const { clientWidth, clientHeight } = this.ref.current; console.log(clientWidth, clientHeight); // 应该能拿到正确值 }); } - 如果是依赖图片或异步资源,可以监听元素的
resize事件,或者监听资源加载完成的事件后再获取尺寸; - 如果是函数组件,用
useEffect结合requestAnimationFrame,效果和上面类似。
内容的提问来源于stack exchange,提问作者Wiki Wong
相关产品推荐
相关产品推荐

