将Vanilla JS视差效果转为React时遇TypeError问题求助
解决React中视差迁移的TypeError: Cannot set properties of undefined (setting 'top')问题
问题原因
- useRef访问DOM节点错误:React中
useRef创建的引用,需通过.current属性才能访问真实DOM元素,直接使用textRef.style会返回undefined——因为textRef本身是包含current属性的对象,而非DOM节点。 - Next.js Image组件的ref兼容问题:你使用的
<Image>是Next.js封装的React组件,默认不支持直接传递ref获取DOM元素,需通过特定方式获取内部的img节点。
修复方案
1. 修正useRef的DOM访问逻辑
所有ref.style需改为ref.current.style,同时添加判断确保current存在,避免组件未挂载/卸载时出现错误。
2. 处理Next.js Image的ref传递
通过React.forwardRef封装Next.js的<Image>组件,使其支持ref传递;同时在useEffect中添加事件清理逻辑,防止内存泄漏。
修复后的完整代码
import { useRef, useEffect } from 'react'; import Image from 'next/image'; // 封装支持ref传递的Next.js Image组件 const ForwardedImage = React.forwardRef((props, ref) => { return <Image {...props} ref={ref} />; }); function ParallaxHero() { const textRef = useRef(null); const sunRef = useRef(null); const houseRef = useRef(null); const bird1Ref = useRef(null); const bird2Ref = useRef(null); const forestRef = useRef(null); const iconRef = useRef(null); const waterRef = useRef(null); useEffect(() => { const handleScroll = () => { const value = window.scrollY; // 确保DOM节点存在后再修改样式 if (textRef.current) { textRef.current.style.top = `${50 + value * -0.5}%`; } if (iconRef.current) { iconRef.current.style.marginTop = `${value * 0.7}px`; } if (sunRef.current) { sunRef.current.style.marginTop = `${value * -2}px`; } if (houseRef.current) { houseRef.current.style.marginTop = `${value * -0.7}px`; } if (bird1Ref.current) { bird1Ref.current.style.top = `${value * -1.5}px`; bird1Ref.current.style.left = `${value * 2}px`; } if (bird2Ref.current) { bird2Ref.current.style.top = `${value * -1.5}px`; bird2Ref.current.style.left = `${value * -5}px`; } if (waterRef.current) { waterRef.current.style.top = `${value * 0.5}px`; } }; window.addEventListener("scroll", handleScroll); // 组件卸载时清理滚动事件监听 return () => { window.removeEventListener("scroll", handleScroll); }; }, []); return ( <> <div className='parallax relative'> <h1 ref={textRef} className='parallax-text'>BOOKINGS, IN A HEARTBEAT.</h1> <ForwardedImage ref={sunRef} className="parallax-image" src={sunImage} layout="fill" objectFit="cover" alt="Sun Image" /> <ForwardedImage ref={houseRef} className="parallax-image" src={houseImage} layout="fill" objectFit="cover" alt="House Image" /> <ForwardedImage ref={bird1Ref} className="parallax-image" src={birdOneImage} layout="fill" objectFit="cover" alt="Bird 1 Image" /> <ForwardedImage ref={bird2Ref} className="parallax-image" src={birdTwoImage} layout="fill" objectFit="cover" alt="Bird 2 Image" /> <ForwardedImage ref={forestRef} className="parallax-image" src={forestImage} layout="fill" objectFit="cover" alt="Forest Image" /> <a href="#" ref={iconRef} className='parallax-button'>Explore</a> <ForwardedImage ref={waterRef} className="parallax-image" src={waterImage} layout="fill" objectFit="cover" alt="Water Image" /> </div> </> ); }
额外注意事项
- 初始化ref时赋值
null,保持类型一致性; - 操作DOM前必须检查
ref.current是否存在,避免未挂载/卸载状态下的报错; - 务必清理滚动事件监听,防止内存泄漏;
- 若使用Next.js 13+ App Router,需确保
<Image>组件的ref传递逻辑符合新版本要求。
内容的提问来源于stack exchange,提问作者Whiz
相关产品推荐
相关产品推荐

