React 67장 - scroll 이벤트의 렌더링
포스트
취소

React 67장 - scroll 이벤트의 렌더링

React

참고자료1 참고자료2

스크롤 이벤트

  • 스크롤 여부에 따라 Nav bar의 스타일이 달라지는 경우가 있다.
  • documnet의 스크롤 여부를 판단하는 것에 대한 예제 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const [scroll, setScroll] = useState < boolean > false;

useEffect(() => {
  const scrollHandler = () => {
    if (window.scrollY >= 100) {
      setScroll(true);
    } else {
      setScroll(false);
    }
  };

  window.addEventListner("scroll", scrollHandler);

  return () => window.removeEventListner("scroll", scrollHandler);
}, []);

// 스크롤 상태값에 의한 스타일 지정
  • 윈도우의 스크롤 이벤트가 발생할 때, scrollHandler 함수를 사용하여 스크롤에 따른 상태값을 변경한다.
  • removeEventListner로 이벤트를 제거해주지 않을 시, 이벤트 리스너가 계속 추가되며 메모리에 영향을 끼칠 수 있기 때문에 이벤트 리스너를 해제해주어야 한다.

렌더링

  • 스크롤의 좌푯값을 파악하여 상태값을 바꿔줄 수 있지만, 매 스크롤마다 렌더링이 발생한다.
  • useEffect에 콘솔을 추가해보면 스크롤이 발생할 때마다 콘솔이 끊임없이 발생하는 것을 볼 수 있다.
  • 불필요한 렌더링이 발생하기 때문에 이에 대한 해결 방법이 필요했다.

렌더링 개선

useCallback

  • 가장 대표적인 방법이지만 useEffect안에서 사용할 수 없기 때문에 제외

debounce

  • 이 함수는 일정 시간동안 이벤트의 반복 호출을 방지하는 방법이다.
  • 분명히 활용에 용이한 곳이 있겠지만, 스크롤이 반복될 때에도 실행되야 하기 때문에 방법은 제외했다.
  • 예제 코드는 아래와 같다.
  • debounce 함수를 실행하고, 원하는 시간값을 입력한다.(ms 단위)
  • 200ms동안 중복 이벤트가 발생되는 것을 방지한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
useEffect(() => {
  const scrollHandler = debounce(() => {
    if (window.scrollY >= 100) {
      setScroll(true);
    } else {
      setScroll(false);
    }
  }, 200);

  window.addEventListner("scroll", scrollHandler);

  return () => window.removeEventListner("scroll", scrollHandler);
}, [scroll]);

requestAnimationFrame(callback),cancelAnimationFrame

  • 두 함수는 주로 애니메이션, 스크롤 이벤트와 같은 브라우저 애니메이션을 다룰 때 사용하는 함수이다.
  • requestAnimationFrame은 브라우저에 실행하고 싶은 애니메이션을 알리고, 다음 리페인트가 진행되기 전에 callback을 실행해서 해당 애니메이터를 업데이트 하도록 한다.
  • 스크롤을 읽는 작업을 현재 프레임에서 실행하고, 다음 렌더링이 발생할 때에 해당 콜백을 같이 넘겨주어 다음 프레임에서 콜백 함수를 같이 실행하도록 하기 떄문에 렌더링을 줄일 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
useEffect(() => {
  let animationFrameId: number;

  const scrollHandler = () => {
    cancelAnimationFrame(animationFrameId);

    animationFrameId = requestAnimationFrame(() => {
      if (window.scrollY > 100 && !scroll) {
        setScroll(true);
      } else if (window.scrollY <= 100 && scroll) {
        setScroll(false);
      }
    });
  };

  window.addEventListener("scroll", scrollHandler);

  return () => {
    window.removeEventListener("scroll", scrollHandler);
    cancelAnimationFrame(animationFrameId);
  };
}, [scroll]);

2024-01-15

  • 다음 프레임에서 실행되도록 하기 때문에 경우에 따라서 이벤트가 실행되지 않을 수도 있다.
  • 새로고침 시, 현재 내가 보고 있는 페이지를 기준으로 화면이 로드된다면 scroll을 실행하고 새로고침 했을 때, 이벤트가 등록되지만 아직 행위가 일어나지 않아서 스크롤은 다시 0으로 잡히게 된다.
  • 따라서 조금이라도 마우스를 움직여주어야만 이벤트가 발생하게 되는데 이를 방지하기 위해 최초 실행을 시켜준다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
useEffect(() => {
  let animationFrameId: number;

  const scrollHandler = () => {
    cancelAnimationFrame(animationFrameId);

    animationFrameId = requestAnimationFrame(() => {
      if (window.scrollY > 100 && !scroll) {
        setScroll(true);
      } else if (window.scrollY <= 100 && scroll) {
        setScroll(false);
      }
    });
  };

  scrollHandler();

  window.addEventListener("scroll", scrollHandler);

  return () => {
    window.removeEventListener("scroll", scrollHandler);
    cancelAnimationFrame(animationFrameId);
  };
}, [scroll]);
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.