클로저와 고차 함수의 개념이 사용된 코드를 볼 때마다 바로 읽히지 않더라고요.
그래서 기록으로 남기고 자주 보면서 익숙해지려고 해요.
디바운스(debounce)는 빠르게 연속으로 발생하는 이벤트 중 마지막 이벤트만 실행되도록 제어하는 기술이에요.
export const debounce = (fn, wait) => {
let timeout = null;
return (...args) => {
const later = () => {
timeout = -1;
fn(...args);
};
if (timeout) {
clearTimeout(timeout);
}
timeout = window.setTimeout(later, wait);
};
};
const handleChangeInput = debounce(event => {
setState(event.target.value);
}, 300);
"handleChangeInput의 매개변수는 debounce에서 반환된 함수의 매개변수와 같다." 라는 생각을 계속 가지고 있으면 읽기 수월해져요.
const handleChangeInput = (...args) => {
const later = () => {
timeout = -1;
fn(...args);
};
if (timeout) {
clearTimeout(timeout);
}
timeout = window.setTimeout(later, wait);
}
handleChangeInput은 이름에서 유추할 수 있듯, input 요소에서 onChange={handleChangeInput} 이런식으로 event를 받아오는 것을 생각해볼 수 있어요.
<input onChange={handleChangeInput} />
// 내부적으로는 ↓ 이렇게 호출되는 것과 같기 때문에 ...args는 event
handleChangeInput(event);
onChange={handleChangeInput}은 input 요소에서 발생한 이벤트 객체를 자동으로 넘겨줘요.
즉, handleChangeInput(event)처럼 호출되는 것과 같아요.
그래서 handleChangeInput 함수의 매개변수 ...args는 event를 받는 구조가 되는 거에요.
export const debounce = (
fn, // (event) => { setTitle(event.target.value) }
wait // 300
) => {
let timeout = null;
return (...args) => { // ...args는 event
const later = () => {
timeout = -1;
fn(...args); // `fn(event)`
};
if (timeout) {
clearTimeout(timeout);
}
timeout = window.setTimeout(later, wait);
};
};
fn은 함수
debounce의 첫 번째 매개변수인 fn은 함수 (event) => { setTitle(event.target.value) }가 돼요.
여기서 ...args는 event가 전달되고, fn(...args)는 결국 fn(event)가 되는 거에요.
즉, event는 그대로 fn의 매개변수로 들어가는 구조에요.
사용자가 input에 값을 입력하면, 브라우저는 onChange 핸들러에 event 객체를 넘겨 호출하고 setState로 state를 바꿔줘요.
<input onChange={handleChangeInput} />
const handleChangeInput = (event) => {
setState(event.target.value);
}
debounce를 사용하면 debounce를 호출할 때 내부의 반환되는 부분이 실행돼요.
const handleChangeInput = debounce(event => {
setState(event.target.value);
}, 300);
export const debounce = (fn, wait) => {
let timeout = null;
return (...args) => {
const later = () => {
timeout = -1;
fn(...args);
};
if (timeout) {
clearTimeout(timeout);
}
timeout = window.setTimeout(later, wait);
};
};
위에서 한 번 얘기했듯 아래의 코드와 같다고 보면 돼요.
let timeout = null;
const handleChangeInput = (...args) => {
const later = () => {
timeout = -1;
fn(...args);
};
if (timeout) {
clearTimeout(timeout);
}
timeout = window.setTimeout(later, wait);
}
여기서 ...arg는 event와 같고, fn은 (event) => { setState(event.target.value) }와 같으므로 아래의 코드가 돼요.
wait은 위의 코드에서 300이 들어왔으니 300으로 해주겠습니다.
<input onChange={handleChangeInput} />
let timeout = null;
const handleChangeInput = (event) => {
const later = () => {
timeout = -1; // setTimeout 시간에 맞춰 timeout도 줄여주기
setState(event.target.value);
};
if (timeout) {
clearTimeout(timeout); // 이전 `setTimeout`을 취소
}
timeout = window.setTimeout(later, 300); // 300ms 이후에 setState 실행
}
그럼 input에 입력이 있을 때마다 300ms의 시간을 가진 setTimeout이 생성되고 300ms 이후에 setState이 state로 입력값으로 바꿔요.
하지만 만약 timeout이 있으면 clearTimeout로 이전 setTimeout을 취소하고, 새로운 타이머를 300ms로 설정해요.