You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:44:53