
本文介绍使用 useref 和 useeffect 在 react 中精准捕获 dom 元素(如 .textcontainer)的实时高度,并将其安全传递给依赖该尺寸的动画组件,解决因渲染时机导致的 null 或未定义问题。
本文介绍使用 useref 和 useeffect 在 react 中精准捕获 dom 元素(如 .textcontainer)的实时高度,并将其安全传递给依赖该尺寸的动画组件,解决因渲染时机导致的 null 或未定义问题。
在 React 中直接通过 document.querySelector 获取元素高度(尤其在模块顶层或 window.onload 中)极易失败——因为此时 DOM 尚未由 React 渲染完成,目标节点不存在,导致 divElement 为 null。正确做法是将 DOM 引用与组件生命周期绑定:使用 useRef 创建可持久访问的 DOM 引用,并在 useEffect 中确保组件已挂载、DOM 已就绪后再读取尺寸。
以下为完整实现方案:
1. 在 IntroText 组件中绑定 ref 并采集高度
import { useRef, useEffect, useState } from 'react';
const IntroText = () => {
const divRef = useRef<htmldivelement>(null);
const [height, setHeight] = useState<number null>(null);
useEffect(() => {
if (divRef.current) {
// 使用 offsetHeight 获取包含 padding 的可见高度
const h = divRef.current.offsetHeight;
setHeight(h + 80); // 按需叠加间距(如题中 +80)
}
}, []); // 空依赖数组 → 仅在挂载后执行一次
return (
<div ref="{divRef}" classname="textContainer" id="textContainer">
{/* 文本内容 */}
</div>
);
};
export default IntroText;</number></htmldivelement>
2. 将高度安全传递给动画组件(如 Video)
由于 height 是状态值,需通过 props 或 Context 传递。推荐在父组件中统一管理并向下透传:
// 假设 Video 和 IntroText 同属 App 或 Layout 组件
const Layout = () => {
const [textDivHeight, setTextDivHeight] = useState<number null>(null);
// 可通过回调函数从 IntroText 通知高度(更解耦)
const onTextHeightReady = (h: number) => {
setTextDivHeight(h);
};
return (
<introtext onheightready="{onTextHeightReady}"></introtext>
{textDivHeight !== null && (
<video initialy="{-textDivHeight}"></video>
)}
>
);
};</number>
对应更新 IntroText,支持回调通知:
const IntroText = ({ onHeightReady }: { onHeightReady: (h: number) => void }) => {
const divRef = useRef<htmldivelement>(null);
useEffect(() => {
if (divRef.current) {
const h = divRef.current.offsetHeight + 80;
onHeightReady(h);
}
}, [onHeightReady]);
return <div ref="{divRef}" classname="textContainer">...</div>;
};</htmldivelement>
Video 组件接收动态初始值:
import { motion } from 'framer-motion';
interface VideoProps {
initialY: number;
}
const Video = ({ initialY }: VideoProps) => {
return (
<motion.div initial="{{" y: initialy animate="{{" transition="{{" delay: duration:><motion.video classname="playVideo" autoplay muted loop><source src="../../src/assets/video.mp4"></source></motion.video></motion.div>
);
};
export default Video;
✅ 关键注意事项:
- ❌ 避免在模块顶层或 window.onload 中操作 DOM —— React 控制渲染流程,时机不可控;
- ✅ useRef + useEffect 是标准解法:ref 提供稳定引用,useEffect 保证 DOM 就绪;
- ✅ 使用 offsetHeight(含 padding/border)而非 clientHeight(不含 border),更符合视觉布局需求;
- ⚠️ 若需响应窗口缩放(如题中“窄屏高度变化”),可在 useEffect 中添加 resize 监听器,并配合 debounce 防抖;
- ? 对响应式场景,建议结合 useResizeObserver(如 @react-hook/resize-observer)替代手动监听,更高效可靠。
通过此模式,你不仅能准确获取初始高度,还能构建可扩展、可维护的尺寸感知逻辑,为复杂交互动画提供坚实基础。











