skip to Main Content

I’m new with react. I’m trying to convert my vanilla js parallax effect in to react but I’m getting this error "TypeError: Cannot set properties of undefined (setting ‘top’)".

Here is my vanilla js script:

<script>
    let text = document.getElementById('text');
    let sun = document.getElementById('sun');
    let house = document.getElementById('house');
    let bird1 = document.getElementById('bird1');
    let bird2 = document.getElementById('bird2');
    let forest = document.getElementById('forest');
    let btn = document.getElementById('btn');
    let header = document.getElementById('header');

    window.addEventListener('scroll', function(){
        let value = window.scrollY;
        text.style.top = 50 + value * -0.5 + '%';
        btn.style.marginTop = value * .7 + 'px';
        sun.style.marginTop = value * -2 + 'px';
        house.style.marginTop = value * -0.7 + 'px';
        bird1.style.top = value * -1.5 + 'px';
        bird1.style.left = value * 2 + 'px';
        bird2.style.top = value * -1.5 + 'px';
        bird2.style.left = value * -5 + 'px';
        header.style.top = value * 0.5 + 'px';
    })
</script>

What I’ve tried in react is this:

function ParallaxHero() {

 const textRef = useRef();
 const sunRef = useRef();
 const houseRef = useRef();
 const bird1Ref = useRef();
 const bird2Ref = useRef();
 const forestRef = useRef();
 const iconRef = useRef();
 const waterRef = useRef();

 useEffect(() => {
   window.addEventListener("scroll", () => {
    let value = window.scrollY;
    textRef.style.top = 50 + value * -0.5 + '%';
    iconRef.style.marginTop = value * .7 + 'px';
    sunRef.style.marginTop = value * -2 + 'px';
    houseRef.style.marginTop = value * -0.7 + 'px';
    bird1Ref.style.top = value * -1.5 + 'px';
    bird1Ref.style.left = value * 2 + 'px';
    bird2Ref.style.top = value * -1.5 + 'px';
    bird2Ref.style.left = value * -5 + 'px';
    waterRef.style.top = value * 0.5 + 'px';
  })
})

return (
  <>
   <div className='parallax relative'>
    <h1 ref={textRef} className='parallax-text'>BOOKINGS, IN A HEARTBEAT.</h1>
    <Image ref={sunRef} className="parallax-image" src={sunImage} layout="fill" objectFit="cover" alt="Sun Image" />
    <Image ref={houseRef} className="parallax-image" src={houseImage} layout="fill" objectFit="cover" alt="Sun Image" />
    <Image ref={bird1Ref} className="parallax-image" src={birdOneImage} layout="fill" objectFit="cover" alt="Sun Image" />
    <Image ref={bird2Ref} className="parallax-image" src={birdTwoImage} layout="fill" objectFit="cover" alt="Sun Image" />
    <Image ref={forestRef} className="parallax-image" src={forestImage} layout="fill" objectFit="cover" alt="Sun Image" />
    <a href="#" ref={iconRef} className='parallax-button'>Explore</a>
    <Image ref={waterRef} className="parallax-image" src={waterImage} layout="fill" objectFit="cover" alt="Sun Image" />
   </div>
  </>
 )
}

Thank you in advance.

2

Answers


  1. Looks like you need to study ‘useRef’ more in detail https://react.dev/reference/react/useRef#usage

    Also, keep a note when you will change the object like textRef.current.style.top, it won’t trigger the rendering cycle as it is not mutating the original object. The object is always referred to with ‘current’, so any value change will go to the same object.

    Try below code:

    import { useRef, useEffect } from 'react';
    
    export default function Counter() {
      let ref = useRef({style:{}});
    useEffect(()=>{
      ref.current.style.top = 2;
      console.log("Prop: ", ref.current.style.top)
    })
    }
    
    Login or Signup to reply.
  2. A ref is an object that always contains the current reference to the element, in the ‘current’ property.

    Also note that the event listener that you add to window needs to be cleaned up after an unmount, you can do this by returning a cleanup function in the useEffect. (otherwise .style would be accessed on undefined and you would get a bunch of "Cannot read properties of undefined" errors)

    function ParallaxHero() {
    
      const textRef = useRef();
      const sunRef = useRef();
      const houseRef = useRef();
      const bird1Ref = useRef();
      const bird2Ref = useRef();
      const forestRef = useRef();
      const iconRef = useRef();
      const waterRef = useRef();
    
      useEffect(() => {
        let scrollHandler = () => {
          let value = window.scrollY;
          textRef.current.style.top = 50 + value * -0.5 + '%';
          iconRef.current.style.marginTop = value * .7 + 'px';
          sunRef.current.style.marginTop = value * -2 + 'px';
          houseRef.current.style.marginTop = value * -0.7 + 'px';
          bird1Ref.current.style.top = value * -1.5 + 'px';
          bird1Ref.current.style.left = value * 2 + 'px';
          bird2Ref.current.style.top = value * -1.5 + 'px';
          bird2Ref.current.style.left = value * -5 + 'px';
          waterRef.current.style.top = value * 0.5 + 'px';
        }
        window.addEventListener("scroll", scrollHandler);
        return ()=>{
          window.removeEventListener("scroll",scrollHandler);
        }
      })
    
      return (
        <>
        <div className='parallax relative'>
          <h1 ref={textRef} className='parallax-text'>BOOKINGS, IN A HEARTBEAT.</h1>
          <Image ref={sunRef} className="parallax-image" src={sunImage} layout="fill" objectFit="cover" alt="Sun Image" />
          <Image ref={houseRef} className="parallax-image" src={houseImage} layout="fill" objectFit="cover" alt="Sun Image" />
          <Image ref={bird1Ref} className="parallax-image" src={birdOneImage} layout="fill" objectFit="cover" alt="Sun Image" />
          <Image ref={bird2Ref} className="parallax-image" src={birdTwoImage} layout="fill" objectFit="cover" alt="Sun Image" />
          <Image ref={forestRef} className="parallax-image" src={forestImage} layout="fill" objectFit="cover" alt="Sun Image" />
          <a href="#" ref={iconRef} className='parallax-button'>Explore</a>
          <Image ref={waterRef} className="parallax-image" src={waterImage} layout="fill" objectFit="cover" alt="Sun Image" />
        </div>
        </>
      )
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search