Debounce with React (Custom Hook & lodash)

wookhyung·2022년 9월 14일
3

React

목록 보기
1/6

최근 과제 테스트를 진행하면서 있었던 디바운스 기능이 포함된 검색창 구현을 하면서 배운 내용에 대해 정리하고자 한다.

🤷‍♂️ Debounce란?

Debounce는 연이어 발생한 이벤트를 하나의 그룹으로 묶어서 처리하는 방식으로, 주로 그룹에서 마지막, 혹은 처음에 처리된 함수를 처리하는 방식으로 사용되곤 한다.

🤔 왜 Debounce를 사용해야 할까?

디바운싱은 주로 ajax 요청에 자주 사용된다. 구글맵 API 같은 유료 API를 사용할 때, 한 글자마다 쿼리를 날리게 되면 비용적인 측면에서 엄청난 손해를 볼 수 있다. 또는 개인 API를 사용할 때에도 사용자가 입력을 할 때마다 API 요청을 한다면 수 많은 호출이 발생하여 성능적으로나, 비용적으로나 좋지 않은 영향을 끼치게 된다.

그렇기 때문에, 사용자는 대부분 한 번에 검색어를 입력하는 걸 생각해본다면 한 글자씩 입력할 때마다 요청을 보내기 보다는 마지막 입력이 들어왔을 때 API 호출을 보내기 위해 Debounce 기능을 사용한다.

JS 예제

let timer;
document.querySelector('#input').addEventListener('input', function(e) {
  if (timer) {
    clearTimeout(timer);
  }
  timer = setTimeout(function() {
    console.log('여기에 ajax 요청', e.target.value);
  }, 200);
});

위는 자바스크립트의 Debounce 예제이다.

본 포스팅에서는 리액트에서 Debounce를 어떻게 구현할지에 대해 작성 할 것이므로 다음으로 넘어가자.


🌐 리액트에서 Debounce 구현하기

1️⃣ Debounce Custom Hook 직접 구현

리액트의 useEffect, useCallback 등의 Hook을 이용하여 Debouce 함수를 직접 구현할 수 있다.

import { useCallback, useEffect } from 'react';

function useDeboucedEffect<T>(func: () => void, delay: number, deps: T) {
  const callback = useCallback(func, [deps]);

  useEffect(() => {
    const timer = setTimeout(() => {
      callback();
    }, delay);

    return () => {
      clearTimeout(timer);
    };
  }, [callback, delay]);
}

export default useDeboucedEffect;

Debounce 기능은 여럿 입력 창에서 공통적으로 사용될 수 있기 때문에 별도로 useDebounce라는 Custom Hook을 만들었다.

첫 번째 인자로 전달받는 func는 debounce 기능이 적용되어 실행될 함수이고, 두 번째 인자인 delay는 debounce가 적용될 딜레이, 마지막 인자인 deps는 useCallback의 의존성으로, 의존성이 변경되었을 때만 func가 변경된다.

useEffect 함수 내부에서는 delay가 끝났을 경우에, callback 함수가 실행되고 delay 기간 중에 callback 혹은 delay 값이 업데이트가 되었다면, cleanup function이 실행되어 타이머를 초기화 시킨다.

// SearchArea.tsx

const SearchArea = () => {
  const [searchValue, setSearchValue] = useState<string>('');

  const handleSearchValue = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchValue(e.target.value);
  };

  useDebouncedEffect(
    () => {
      somethingFunc();
    },
    300,
    searchValue,
  );

  return (
      <SearchForm isFocus={isFocus}>
        <SearchIcon icon={faSearch} />
        <SearchInput onChange={handleSearchValue} />
      </SearchForm>
  );
};

2️⃣ lodash 라이브러리 사용

대부분 그렇듯, 이미 라이브러리를 통해서 기능이 구현되어 있는 경우가 많다..

Lodash에는 이미 debounce 함수가 구현되어 있고, 사용하는 방법도 간단하다.

Arguments

func (Function): The function to debounce.
wait=0: The number of milliseconds to delay.
options={}: The options object.
options.leading=false: Specify invoking on the leading edge of the timeout.
options.maxWait: The maximum time func is allowed to be delayed before it's invoked.
options.trailing=true: Specify invoking on the trailing edge of the timeout.

Returns

(Function): Returns the new debounced function.

_.debounce(func, [wait=0], [options={}])

lodash Debounce 예제

import React from 'react';
import _ from 'lodash';

const Input = () => {
    const debounceFunc = _.debounce((value) => {
        somethingFunc(value);
        ...
    }, 1000);
  
    const onChange = (e) => {
        debounceFunc(e.target.value);
    }
  
    return <input type="text" onChange={onChange} />;    
};

참고

https://usehooks-ts.com/react-hook/use-debounce
https://lodash.com/docs/4.17.15#debounce
https://developer-talk.tistory.com/248
https://timmousk.com/blog/lodash-debounce/

profile
Front-end Developer

0개의 댓글