짧은 간격으로 연이어 호출되는 함수들 중 마지막 함수만 호출되도록 하는 것
마지막 함수가 호출된 후 일정 시간이 지나기 전까지 다시 호출되지 않도록 하는 것
import type { NextPage } from 'next'
import React, {useCallback, useEffect, useMemo, useState} from 'react'
import {debounce, throttle} from "lodash"
// ref: https://dmitripavlutin.com/react-throttle-debounce/
const TDOnePage: NextPage = () => {
// debounce
const [value, setValue] = useState("")
const logInput = useCallback((newValue: string)=>{
console.log(newValue)
},[])
// as default option,
// leading: false
// trailing: true
const debouncedLogInput = useMemo(()=>debounce(logInput, 500), [logInput])
useEffect(()=>{
debouncedLogInput(value)
// logInput(value)
},[debouncedLogInput, value])
const handleChangeInput: React.ChangeEventHandler<HTMLInputElement> = useCallback((event)=>{
setValue(event.currentTarget.value)
},[])
// throttle
const logScroll = useCallback(()=>{
console.log("scrolling")
},[])
// as default option,
// leading: true
// trailing: true
const throttledLogScroll = useMemo(()=>throttle(logScroll, 2000), [logScroll])
const handleScroll = useCallback(()=>{
throttledLogScroll()
},[throttledLogScroll])
useEffect(() => {
console.log("window", window)
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [handleScroll])
useEffect(()=>{
return () => {
debouncedLogInput.cancel()
throttledLogScroll.cancel()
}
},[debouncedLogInput, throttledLogScroll])
return (
<div style={{height: 5000}}>
<input onChange={handleChangeInput}/>
</div>
)
}
export default TDOnePage
import type { NextPage } from 'next'
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
const TDTwoPage: NextPage = () => {
// debounce
const [value, setValue] = useState("")
const logInput = useCallback((newValue: string)=>{
console.log(newValue)
},[])
const logInputRef = useRef(logInput)
useEffect(()=>{
logInputRef.current = logInput
}, [logInput])
const [debouncedLogInput, cancelDebouncedLogInput] = useMemo(()=>
debounce((...args: Parameters<typeof logInput>)=>logInputRef.current(...args), 500)
, [])
useEffect(()=>{
return () => {
cancelDebouncedLogInput()
}
},[cancelDebouncedLogInput])
useEffect(()=>{
debouncedLogInput(value)
// logInput(value)
},[debouncedLogInput, value])
const handleChangeInput: React.ChangeEventHandler<HTMLInputElement> = useCallback((event)=>{
setValue(event.currentTarget.value)
},[])
// throttle
const logScroll = useCallback(()=>{
console.log("scrolling")
},[])
const logScrollRef = useRef(logScroll)
useEffect(()=>{
logScrollRef.current = logScroll
}, [logScroll])
const [throttledLogScroll, cancelThrottledLogScroll] = useMemo(()=>
throttle((...args: Parameters<typeof logScroll>)=>logScrollRef.current(...args), 500)
, [])
useEffect(()=>{
return () => {
cancelThrottledLogScroll()
}
},[cancelThrottledLogScroll])
const handleScroll = useCallback(()=>{
throttledLogScroll()
},[throttledLogScroll])
useEffect(() => {
console.log("window", window)
window.addEventListener('scroll', handleScroll)
return () => {
window.removeEventListener('scroll', handleScroll)
}
}, [handleScroll])
return (
<div style={{height: 5000}}>
<input onChange={handleChangeInput}/>
</div>
)
}
export default TDTwoPage
const debounce = <Callback extends (...args: any) => any>(callback: Callback, delay: number) => {
let timeOutId: ReturnType<typeof setTimeout> | null = null;
// make order to call callback after delay,
// but if it's called again within delay,
// it increase entire delay
const debouncedCallback = (...args: Parameters<Callback>) => {
if (timeOutId) {
clearTimeout(timeOutId)
}
timeOutId = setTimeout(()=>{
callback(...args)
timeOutId = null
}, delay)
}
const cancel = () => {
if (!timeOutId) return
clearTimeout(timeOutId)
}
return [debouncedCallback, cancel] as const
}
const throttle = <Callback extends (...args: any) => any>(callback: Callback, delay: number) => {
let timeOutId: ReturnType<typeof setTimeout> | null = null;
// make order to call callback after delay,
// at the same time, it disabled incoming call for it's delay
const throttledCallback = (...args: Parameters<Callback>) => {
if (timeOutId) return;
timeOutId = setTimeout(()=>{
callback(...args)
timeOutId = null
}, delay)
}
const cancel = () => {
if (!timeOutId) return
clearTimeout(timeOutId)
}
return [throttledCallback, cancel] as const
}