React Debounce

hyunheal·2022년 2월 11일
0

React

목록 보기
5/6

react 프로젝트에서 debounce를 할 일이 있었다.
그래서 정리한다.

Debounce?

debounce란 어떤 이벤트가 발생하고 특정 시간 안에 다시 발생하면 이전 이벤트를 무시하고 마지막 이벤트만 처리하는 것을 말한다.
throttle과 비슷한데 throttle은 이벤트가 n초에 한번만 호출될 수 있게 하는거다.

동작이 어떻게 다르냐면
debounce는 특정시간 안에 계속 이벤트를 발생시키면 계속 이벤트처리가 안되지만
throttle은 계속 이벤트를 발생시키면 n초마다 한번씩 처리된다.

구현

setTimeout을 이용해서 구현했다.
다음은 커스텀훅으로 만든 useDebounce이다.

// useDebounce.ts
import { useState } from 'react'

const useDebounce = () => {
  const [timer, setTimer] = useState<ReturnType<typeof setTimeout>>()
  const [callbackResult, setCallbackResult] = useState<any>()

  const dispatchDebounce = (callback: () => Promise<any>, term: number): void => {
    if (timer) {
      clearTimeout(timer)
    }

    const newTimer = setTimeout(async () => {
      const result = await callback()
      setCallbackResult(result)
    }, term)
  
    setTimer(newTimer)
  }

  return [
    callbackResult,
    dispatchDebounce
  ]
}

export default useDebounce

clearTimeout을 이용할 수 있게 timer이란 상태를 만들어줬고
디바운싱할 함수를 받아서 그 결과값을 반환할 수 있게 했다.
dispatchDebounce함수가 호출됐을 때 timer가 설정되어있으면 클리어하고 setTimeout으로 새로 timer를 설정한다.

다음은 input에 useDebounce를 적용한 코드이다.

// debounce.tsx
import type { NextPage } from 'next'
import React, { useState, useRef } from 'react'
import useDebounce from './useDebounce'

type oddevenType = 'odd' | 'even' | '0'

const Debounce: NextPage = () => {
  const [triggered, setTriggered] = useState<number>(0)
  const [debounceTerm, setDebounceTerm] = useState<number>(500)
  const [helperText, setHelperText] = useState<string>('')
  
  const [callbackResult, dispatchDebounce] = useDebounce()
  const debounceTermRef = useRef<HTMLInputElement>(null)

  const inputEventHandler = async (e: React.ChangeEvent<HTMLInputElement>): Promise<oddevenType> => {
    const value = e.target.value

    await new Promise((r => setTimeout(r, 1)))

    setTriggered(v => v+1)
    if (value.length === 0) {
      return '0'
    } else if (value.length % 2) {
      return 'odd'
    } else {
      return 'even'
    }
  }

  const debounceTermHandler = (value: string | number): void => {
    const term = Number(value)

    if (isNaN(term)) {
      setHelperText('only number plz')
    } else {
      setDebounceTerm(term)
      setHelperText('')
    }
  }

  return (
    <>
      <div className="debounce-term-setting">
        <input type="text" ref={debounceTermRef} />
        <button onClick={() => debounceTermHandler(debounceTermRef.current?.value ?? '0')}>change Debounce Term</button>
        <span>{ helperText }</span>
      </div>

      <div className="debounce-result">
        <div className="setting-result">
          <span>term: { debounceTerm }ms</span>
        </div>
        <input 
          type="text" 
          onChange={(e) => {
            dispatchDebounce(() => inputEventHandler(e), debounceTerm)
          } }
        />
        <span>{ callbackResult }</span>
        <div>event triggered: { triggered }</div>
      </div>
    </>
  )
}

export default Debounce

디바운싱을 한 것만 보자면 Promise객체를 만들어서 비동기로 함수를 만들었고, 반환값으로는 input에 입력한 글자의 수가 짝수인지 홀수인지를 알려주게 했다.

추가로 input을 하나 더 만들어서 debounce term을 설정할 수 있게 했다.

결과

https://d3kq1px22hh3hs.cloudfront.net/debounce/debounce

실험실을 하나 만들었당.

profile
FE 초짜

0개의 댓글