如何动态获取React条件渲染组件的高度?
解决条件渲染组件首次显示时基于ref高度的样式不生效问题
你的猜测是对的,问题根源在于React渲染周期与ref挂载的时机差:当你点击按钮切换isClicked为true时,条件渲染的组件会触发首次渲染,但ref的赋值是在DOM节点挂载完成后才执行的。此时你的useEffect依赖ref.current,但首次渲染时ref.current仍为null,导致dimension保持初始值,样式无法生效;直到ref赋值完成后useEffect才会执行更新状态,触发二次渲染,样式才会生效,这就造成了首次显示时的样式异常。
以下是几种可靠的解决方案:
方案1:监听isClicked状态,配合requestAnimationFrame获取尺寸
当isClicked变为true时,组件完成挂载,此时我们可以在监听该状态的useEffect中,用requestAnimationFrame确保DOM完全渲染后再获取尺寸:
import React, { useEffect, useState, useRef } from "react"; const App = () => { const [isClicked, setClick] = useState(false); const [dimension, setDimension] = useState({ width: 0, height: 0 }); const ref = useRef(null); useEffect(() => { if (isClicked && ref.current) { const updateSize = () => { setDimension({ width: ref.current.clientWidth, height: ref.current.clientHeight, }); }; // 确保DOM渲染完成后再获取尺寸 requestAnimationFrame(updateSize); } }, [isClicked]); return ( <div> <button onClick={() => setClick(!isClicked)}>Click me</button> {isClicked && ( <div className="App"> <div ref={ref} style={{ marginTop: dimension.height }}> <h1>Hello</h1> <h2>World</h2> <h3>!</h3> </div> </div> )} </div> ); }; export default App;
方案2:用useLayoutEffect替代useEffect(无闪烁优化)
useLayoutEffect会在DOM更新完成后、浏览器重绘前同步执行,能更早获取到DOM尺寸,避免二次渲染的闪烁问题:
import React, { useLayoutEffect, useState, useRef } from "react"; const App = () => { const [isClicked, setClick] = useState(false); const [dimension, setDimension] = useState({ width: 0, height: 0 }); const ref = useRef(null); useLayoutEffect(() => { if (isClicked && ref.current) { setDimension({ width: ref.current.clientWidth, height: ref.current.clientHeight, }); } }, [isClicked]); return ( <div> <button onClick={() => setClick(!isClicked)}>Click me</button> {isClicked && ( <div className="App"> <div ref={ref} style={{ marginTop: dimension.height }}> <h1>Hello</h1> <h2>World</h2> <h3>!</h3> </div> </div> )} </div> ); }; export default App;
关键说明
两种方案的核心都是在组件挂载完成后(isClicked变为true时),确保DOM已渲染再获取尺寸,通过更新状态触发二次渲染,让样式正确应用。useLayoutEffect适合对视觉体验要求高的场景,能避免样式闪烁;useEffect配合requestAnimationFrame则更通用,不会阻塞浏览器绘制。
内容的提问来源于stack exchange,提问作者Saltuk Kezer
相关产品推荐
相关产品推荐

