
本文介绍使用 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;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} />
{textDivHeight !== null && (
<Video initialY={-textDivHeight} />
)}
</>
);
};对应更新 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>;
};Video 组件接收动态初始值:
import { motion } from 'framer-motion';
interface VideoProps {
initialY: number;
}
const Video = ({ initialY }: VideoProps) => {
return (
<motion.div
initial={{ y: initialY }}
animate={{ y: 0 }}
transition={{ delay: 0.5, duration: 1.2 }} // 示例延时,按需调整
>
<motion.video className="playVideo" autoPlay muted loop>
<source src="../../src/assets/video.mp4" />
</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)替代手动监听,更高效可靠。
通过此模式,你不仅能准确获取初始高度,还能构建可扩展、可维护的尺寸感知逻辑,为复杂交互动画提供坚实基础。


















