react 프로젝트에서 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
실험실을 하나 만들었당.