
리액트에서는 성능 최적화를 위해 최소한의 상태 변경과 최소한의 콜백 호출을 필요로 한다.
예를들어 보자
검색 입력창에서 사용자가 키보드 입력마다 서버에 API요청을 보내게 구현하였다면?
<input type="text" onChange={(e) => fetchSearchResults(e.target.value)} />
사용자가 타이핑을 빠르게 한다면 서버에 과부하가 걸릴 위험이 있다…
브라우저의 창 크기를 알기 위해 브라우저의 resize 이벤트 마다 계산을 하는 함수가 있다고 해보자
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []);
resize이벤트가 호출될때마다 resize함수가 호출되는 것은 성능저하를 일으킬 위험이 있다.
위 같은 불필요한 상태변화와 콜백호출을 줄이는 방법중 대표적인 두가지 방법이 있다.
아래 두가지는 연속된 이벤트호출을 어떻게 해결하는 지에 대한 스킬이다.
이벤트가 연속으로 일어난다면 호출하지 않고 있다가 잠잠해 졌을때 (일정시간동안 이벤트가 안일어날때) 마지막 이벤트를 처리한다.
이벤트가 연속으로 일어난다면 모든 이벤트를 처리 하지 않고 일정 간격으로 이벤트를 처리한다.
글로만 봐서는 이해가 잘 가지 않을수 있다.
실제 코드와 구현 방식을 보며 확인해보자
export default function useDebounce<T>(value: T, delay: number) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}
간단하다 value 상태가 변경될때마다 delay 뒤에 상태가 변경되게 한다.
만약 delay 가 지나기 전에 value의 상태가 변경된다면
delay를 초기화 한다.
function App() {
const [value, setValue] = useState("");
const debouncedValue = useDebounce(value, 500);
return (
<div>
<input
onChange={(e) => setValue(e.target.value)}
className="outline-none border-2 w-full"
type="text"
/>
<div>그냥 값 : {value}</div>
<div>debounce 된 값 : {debouncedValue}</div>
</div>
);
}

export default function useThrottle<T>(value: T, limit: number): T {
const [throttledValue, setThrottledValue] = useState(value);
const lastRan = useRef(Date.now());
useEffect(() => {
const handle = setTimeout(() => {
setThrottledValue(value);
lastRan.current = Date.now();
}, limit - (Date.now() - lastRan.current));
return () => {
clearTimeout(handle);
};
}, [limit, value]);
return throttledValue;
}
나도 처음에 코드를 이해하는데 시간이 걸렸다.
하지만 일정 시간이 흐를때마다 handle함수가 호출될 뿐이라는것…
function App() {
const [value, setValue] = useState("");
const throttledValue = useThrottle(value, 500);
return (
<div>
<input
onChange={(e) => setValue(e.target.value)}
className="outline-none border-2 w-full"
type="text"
/>
<div>그냥 값 : {value}</div>
<div>throttle 된 값 : {throttledValue}</div>
</div>
);
}
