throttle 과 debounce 그리고 lodash 를 사용하여 실제 React 에 적용하는 법. 그리고 useCallback 에서의 lint 에러.

반응형

실제로 throttle 과 debounce 의 경우 debounce 는 검색어 자동완성에 그리고 throttle 은 로딩 모달이 없는 단순 get 요청의 반복 요청의 빈도를 제한하는 정도로 사용된다. 보통은 생성, 수정, 삭제 같은 경우 isSubmitting 플래그로 로딩 모달과 함께 해당 요청의 응답이 없는 동안에는 클릭하지 못하도록 하는 방식을 사용한다. 생성, 수정, 삭제 같은 경우에 throttle, debounce 를 사용할 경우 결국은 이벤트를 두번은 수행하게 되기 때문이다.

 

throttle 과 debounce 그리고 lodash 를 사용하여 실제 React 에 적용하는 법.

 

throttle 은 이벤트 발생 즉시 실행되고 그 다음 지정한 시간 동안 발생되지 못하게 막고 그 시간이 지나면 다시 이벤트를 실행한다. 클릭하면 리프레쉬 하는 로직이 있다고 가정했을 때 5초의 쓰로틀 시간을 설정할 경우 처음 클릭했을 때 바로 실행이 되고 5초 간은 아무리 클릭해도 실행이 되지는 않는다. 하지만 5초 안에 한번이라도 클릭했을 경우 5초가 되는 순간 이벤트가 실행된다. 한번 누르고 5초 간 아예 누르지 않았다면 5초 뒤에 실행이 되지 않는다. 즉, 이벤트는 클릭하는 순간 바로 실행되고 5초간은 실행되지 않다가 클릭이 한번이라도 있으면 5초 뒤에는 실행이 된다. 다시 말하면 클릭하면 즉시 실행되고 5초 안에 여러번 클릭을 했다거나 한번이라도 클릭했다면 딱 5초 지난 뒤에 해당 클릭에 대한 이벤트가 실행이 된다. 반드시 5초 지난 뒤에 클릭해야 하는 것이 아니라 그 5초 안에 한번이라도 클릭이 일어났다면 5초 되는 순간 이벤트가 실행된다. 그 다음에 다시 클릭이 없다면 5초 뒤에 실행이 또 되지는 않을 것이다.  

(infinite scroll 등에 사용)

debounce 는 동일한 이벤트를 받을 경우 지정한 시간동안 실행을 하지 않고 버티다가 지정한 시간이 지나면 발생시킨다. 디바운스 시간으로 200ms 를 지정할 경우 검색어 계속 입력하는데 onChange 가 계속 발생해도 수행되지 않다가  200ms 동안 입력않으면 그때 발생되어 검색어 후보군을 가져와 보여준다. 즉 동일한 이벤트가 200ms 안에 계속 발생하는 동안에는 실행이 되지 않다가 더이상의 이벤트가 발생하지 않게 되면 200ms 가 지난 순간 한번 실행된다.

(검색창 검색어 자동 완성 등에 사용)

둘 다 시간을 2초를 지정했을 경우 debounce 가 throttle 과 다른 점을 보면 debounce 는 클릭해도 바로 실행되지 않고 throttle 은 바로 실행된다. debounce 는 동일 이벤트가 계속 들어오는 동안에는 아예 실행이 되지 않다가 2초 동안 이벤트가 들어오지 않는 순간 한번 실행된다. throttle 은 동일 이벤트가 계속 들어오는 동안 처음 즉시 실행 후 2초 동안 실행 안되다가 최초 실행 후 2초 뒤에 실행이 되고 또 계속 이벤트가 들어온다고 하면 2초마다 매번 실행된다.

 

리액트에서 실제로 useCallback 과 함께 사용하려면 다음과 같은 Lint 에러를 발생시킨다. useCallback 안에 lodash 의 throttle 이나 debounce 로 함수를 감싼 형태일 때 이러한 Lint 에러를 낸다.

 

React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead.(react-hooks/exhausted-deps)

 

클릭을 한다던가 TextInput 에 글자 입력시 onchange 한다던가 하는 등의 이벤트 들에는 throttle 과 debounce 는 거의 필수라고 할 수 있고 이렇게  lodash 의 throttle, debounce 로 감싸주는 방식을 사용하는 것이 best practice 라고 생각한다.

 

해결법:

https://kyleshevlin.com/debounce-and-throttle-callbacks-with-react-hooks

 

import React from 'react'
import { debounce } from 'lodash'

function Search({ onSearch }) {
  const [value, setValue] = React.useState('')

  // This use of `useCallback` has a problem
  const debouncedSearch = React.useCallback(
    debounce(val => {
      onSearch(val)
    }, 750),
    [onSearch]
  )

  const handleChange = React.useCallback(
    e => {
      setValue(e.target.value)
      debouncedSearch(e.target.value)
    },
    [debouncedSearch]
  )

  return <input type="text" value={value} onChange={handleChange} />
}

이렇게 된 것을 useMemo 를 사용해서 이렇게 수정하면 된다.

function Search({ onSearch }) {
  const [value, setValue] = React.useState('')

  // This is the solution
  const debouncedSearch = React.useMemo(
    () =>
      debounce(val => {
        onSearch(val)
      }, 750),
    [onSearch]
  )

  const handleChange = React.useCallback(
    e => {
      setValue(e.target.value)
      debouncedSearch(e.target.value)
    },
    [debouncedSearch]
  )

  return <input type="text" value={value} onChange={handleChange} />
}

 

반응형

댓글

Designed by JB FACTORY