디바운싱 React에서 적용법

KHW·2022년 5월 6일
0

Javascript 지식쌓기

목록 보기
91/95
post-custom-banner

상황

오드컨셉 과제를 하던중
스크롤시 region데이터가 해당 window에서 보이는 위치로 이동되게 만드는 것을 구현하는데 매번 스크롤 이벤트가 불필요하게 발생되는 것을 막기 위해 디바운싱을 생각하게 되었다.

디바운싱 실패한 사례

const FashionContainer = () => {


  useEffect(() => {
    function watchScroll() {
      window.addEventListener("scroll", checkRegions);
    }
    watchScroll();

    ...

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

  ...
  
  const checkRegions = () => {
    let timer;
    if (timer) {
      console.log(timer, "if");
      clearTimeout(timer);
    }
    timer = setTimeout(function () {
      console.log(timer, "setTimeout");
      setRegionHeight(window.pageYOffset);
    }, 2000);

    console.log(timer, "out");
  };

 ...
 
  return (
    <MinWidth>
      <Header />
      {isLoading && <Modal />}
      <FashionSection
        productsData={products}
        regionsData={regions}
        clickBtn={clickBtn}
        regionHeight={regionHeight}
      />
    </MinWidth>
  );
};

const MinWidth = styled.div`
  min-width: 500px;
`;

export default FashionContainer;

주요부분을 제외하고 디바운스 부분만 집중해본다.

  • 현재 timer가 이벤트 핸들러 함수내에서 선언되고 있다.
    해당 결과를 보면 다음과 같다.

이런식으로 setTimeout이 한번의 스크롤만으로 11번이상 실행되는 것을 알 수있으며 추가로 if(timer) 부분이 전혀 실행되지 않는 것을 알 수 있다.
즉, timer를 계속 새로 정의하여 let timer if문에서 인식할 수 없는것이다.

디바운싱 성공한 사례

const FashionContainer = () => {
    let timer;

  useEffect(() => {
    function watchScroll() {
      window.addEventListener("scroll", checkRegions);
    }
    watchScroll();

    ...

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

  ...
  
  const checkRegions = () => {
    if (timer) {
      console.log(timer, "if");
      clearTimeout(timer);
    }
    timer = setTimeout(function () {
      console.log(timer, "setTimeout");
      setRegionHeight(window.pageYOffset);
    }, 2000);

    console.log(timer, "out");
  };

 ...
 
  return (
    <MinWidth>
      <Header />
      {isLoading && <Modal />}
      <FashionSection
        productsData={products}
        regionsData={regions}
        clickBtn={clickBtn}
        regionHeight={regionHeight}
      />
    </MinWidth>
  );
};

const MinWidth = styled.div`
  min-width: 500px;
`;

export default FashionContainer;
  • 현재 timer가 이벤트 핸들러 함수 밖에서 선언되고 있다.
    즉, 이전에 setTimeout의 받아온 timer의 값을 인식하고 있어
    if(timer)에서 값을 인식하여 처리할 수 있다.
    해당 결과를 보면 다음과 같다.

outif만 반복되며 가장 마지막에 setTimeout 이 단한번만 실행된다.
즉, 디바운싱을 제대로 구현하는데는 성공했다.

다른 구글링한 디바운싱들

다른 구글링에서는 React에서 timer를 상태값으로 useState를 사용하는데
사실 이부분은 아직 나도 어렵다.

  • 위처럼 구현은 되는데 굳이 timer를 상태로 하면 재렌더링이 되는데 재렌더링이 timer때문에 필요할 것인가??? 이건 좀 모르겠는 느낌이다.

정리하기

적어도 이벤트 함수 내에서 timer를 선언하는 것은 제대로된 디바운싱 결과를 보여줄수 없다.
디바운싱을 쓰려면 이벤트 함수 밖에 timer를 정의하자!

profile
나의 하루를 가능한 기억하고 즐기고 후회하지말자
post-custom-banner

0개의 댓글