sooleeandtomas

Infinity Scroll (Debounce & Throttle 어떤걸 쓰면 좋을까?) 본문

코딩 공부 노트/React

Infinity Scroll (Debounce & Throttle 어떤걸 쓰면 좋을까?)

sooleeandtomas 2021. 5. 14. 14:15

Debounce  & Throttle

 

엄청나게 많은 양의 이벤트가 일어났을 때 콜백의 수를 줄여줍니다.

온체인지가 일어날 떄 실행되는 콜백의 실행을 효과적으로 줄여줍니다.

 

debounce :

마지막 이벤트로부터 일정 시간을 기다렸다가 이벤트를 수행합니다.

일정시간 내에 같은 이벤트가 또 들어오면 이전 요청은 취소합니다.

[연관검색어, .. ]

 

throttle : 

일정 시간 동안 일어난 이벤트를 모아서 주기적으로 1번 실행해줍니다.

일정 시간 동안 가장 최근의 이벤트를 실행해줍니다.

[스크롤 이벤트 .. ]

 

디바운스와 트로틀을 제공해주는 Lodash를 이용합니다.

import React from "react";
import _ from "lodash";

const Search = () => {
  const debounce = _.debounce((e)=>{
    console.log(e.target.value)
  }, 2000)

  const throttle = _.throttle((e)=>{
    console.log(e.target.value)
  }, 2000)

  const onChange = e => {
    console.log(e.target.value)
  }

  return(
    <div>
      <input type="text" onChange={throttle}/>
    </div>
  )
}

export default Search;

 

주의 ! 함수형 컴포넌트에서 state가 업데이트되면 그 안에 함수들이 모두 업데이트 됩니다.

그래서 debounce가 제대로 작동하지 않습니다.

이를 위해 useCallback을 사용합니다.

함수가 리랜더링 되더라도 debounce는 리랜더링 되지 않도록 해줍니다.

import React, { useCallback, useState } from "react";
import _ from "lodash";

const Search = () => {
  const [text, setText] = useState("");
  const debounce = _.debounce((e)=>{
    console.log(e.target.value)
  }, 2000)

  const keyPress = useCallback(debounce, []); // 함수는 리랜더링이 되더라도 debounce는 초기화 되지 않습니다.

  const throttle = _.throttle((e)=>{
    console.log(e.target.value)
  }, 2000)

  const onChange = e => {
    setText(e.target.value);
    keyPress(e);
  }

  return(
    <div>
      <input type="text" onChange={onChange} value={text}/>
    </div>
  )
}

export default Search;

 


 

예제 : 스크롤 이벤트

함수형 컴포넌트에서 이벤트 종료시키는 법 : useEffect에서 return () => { ... } 

  useEffect(()=>{
    window.addEventListener("scroll", handleScroll);

    return () => window.removeEventListener("scroll", handleScroll)
  },[])

 

window.innerHeight : 모니터 높이 // 메뉴바, 툴바 제외

document.body.scrollHeight : 도큐먼트 전체의 높이  //눈에 안보이는 높이 포함

document.scrollTop : 스크롤이 얼마나 움직였는지


const { innerHeight } = window; // 모니터 창 
const { scrollHeight } = document.body; // 도큐먼트 전체 창
const scrollTop = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
//얼마나 스크롤 했는지

scrollHeight - innerHeight - scrollTop = 스크롤하고 남은 창

if(scrollHeight - innerHeight - scrollTop < 200){
  callNext();
}

 

위에서도 말했듯이 함수형 컴포넌트에서는 리랜더링될때마다 debounce, throttle 도 리랜더링 되기 때문에

이 함수들은 useCallback을 사용해서 따로 기억해줘야 합니다.

  const handleScroll = useCallback(_handleScroll, [is_loading]);

 

_handleScroll을 기억해준 것을 handleScroll이라고 합니다.

  const _handleScroll = _.throttle(()=>{
    if(is_loading){return}

    const { innerHeight } = window;
    const { scrollHeight } = document.body;
    const scrollTop = (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;

    if(scrollHeight - innerHeight - scrollTop < 200){
      callNext();
    }
  }, 300);

 

마지막으로 handleScroll을 이벤트 등록을 해줍니다.

  useEffect(()=>{
    if(is_loading){return}
    if(is_next){
      window.addEventListener("scroll", handleScroll);
    }else{
      return () => window.removeEventListener("scroll", handleScroll)
    }
    return () => window.removeEventListener("scroll", handleScroll)
  },[is_next, is_loading])

 

callNext()에서는 데이터를 불러오는 함수를 사용해줍니다. 

Comments