React에서 Parallax 구현하기

리액트는 컴포넌트 단위로 개발을 한다.

그래서 컴포넌트마다 useEffect 내부에 스크롤 이벤트를 바인딩해서 parallax를 구현해야 하나 싶었는데, 스크롤할 때마다 컴포넌트 개수만큼 이벤트 리스너가 동작하면 아무래도 성능에 영향을 미칠 수밖에 없다.

 

그래서 생각한 첫 번째 방법은

취상위 컴포넌트인 <App />에서만 스크롤 이벤트를 바인딩하고 document.querySelector를 사용해서 DOM을 가져와 parallax를 구현하는 것이었다.

 

그러나,

리액트는 가상 DOM을 비교해 브라우저에 painting을 효율적으로 하기 위한 라이브러리이므로 document.querySelector를 사용한다는 것은 리액트의 본질을 고려하지 않는 방법이다.(사용하면 안 되는 게 아니라 지양해야 하는 것)

 

그래서 두 번째 방법은

첫 번째 방법에서 문제가 되는 document.querySelector을 사용하지 않기 위해 useRef를 사용하는 것이었다.

이것의 문제는 하위 컴포넌트에 있는 element를 <App />으로 가져와야 했기에 컴포넌트마다 ref를 props로 전달해야하는 불편함이 있었다.

 

드디어 찾은 세 번째 방법!!

state가 업데이트되면 리렌더링이 된다는 것을 이용!!

 

그래서 결국에는

<App />에서 스크롤 이벤트를 바인딩하고 스크롤 값을 useState의 set함수로 전달하여 state를 하위 컴포넌트에 props로 전달하는 것으로 parallax를 구현할 수 있었다.

 

[ 간단한 예시 ]

 

const Parallax = ({ posY }) => {
  const [left, setLeft] = useState({
    left: 0,
  });

  useEffect(() => {
    console.log(posY);
    setLeft({
      left: posY,
    });
  }, [posY]);

  return (
    <div className="parallax" style={left}>
      Parallax
    </div>
  );
};

function App() {
  const [posY, setPosY] = useState(0);

  useEffect(() => {
    window.addEventListener("scroll", () => {
      setPosY(window.scrollY);
    });
  }, []);
  
  return (
    <>
      <Parallax posY={posY} />
    </>
  );
}