저번에는 throttle을 사용해 클릭 이벤트를 핸들링해봤다. 이번엔 debounce를 사용해볼 차례다.
throttle에 대한 포스팅이 궁금하다면 ~ --> [JavaScript] debounce & throttle
닥터퐁 채팅창은 이벤트 리스너에 등록된 handleScroll
함수에서 useRef의 스크롤 값으로 데이터 요청과 프리뷰 표시 유무를 처리한다. 해당 함수는 다음과 같다.
const chatsRef = useRef(null);
useEffect(() => {
if (chatsRef.current)
chatsRef.current.addEventListener('scroll', handleScroll);
return () => {
if (chatsRef.current)
chatsRef.current.removeEventListener('scroll', handleScroll);
}
}, []);
const handleScroll = () => {
const ref = chattingsRef.current!;
if (Math.abs(ref.scrollTop) > ref.scrollHeight - ref.clientHeight - 100) {
// 스크롤이 맨 위인 경우 다음 페이지의 데이터 요청
}
if (ref.scrollTop < -50) {
// 스크롤이 맨 아래가 아닌 경우 프리뷰 표시
}
if (ref.scrollTop > -10) {
// 스크롤이 맨 아래인 경우 프리뷰 제거
}
};
return (
<div ref={chatsRef}>
</div>
);
이러면 채팅창에서 스크롤을 드르륵~ 할때마다 위의 함수가 미친듯이 호출된다. 모든 스크롤마다 스크롤의 위치를 확인하는 것은 확실히 비효율적인 방법이다. 때문에 debounce를 사용하기로 결정했다. 추가한 debounce 함수는 다음과 같다.
const debouncer = <T extends Function>(func: T, delay: number) => {
let timer: ReturnType<typeof setTimeout> | null = null;
return (...args: any[]) => {
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(func, delay, ...args);
};
};
위의 코드는 이전의 throttle과 다르게 대기시간 중 발생하는 새로운 이벤트를 기준으로 대기시간이 초기화된다. 무슨 말이냐면, delay를 5초로 설정한 함수 A는 5초 동안 새로운 이벤트가 발생하지 않아야 실행된다는 것이다.
위의 함수를 tsx파일에서 import하여 사용했다.
const chatsRef = useRef(null);
useEffect(() => {
if (chatsRef.current)
chatsRef.current.addEventListener('scroll', handleScroll);
return () => {
if (chatsRef.current)
chatsRef.current.removeEventListener('scroll', handleScroll);
}
}, []);
const handleScroll = debouncer(() => {
const ref = chattingsRef.current!;
if (Math.abs(ref.scrollTop) > ref.scrollHeight - ref.clientHeight - 100) {
// 스크롤이 맨 위인 경우 다음 페이지의 데이터 요청
}
if (ref.scrollTop < -50) {
// 스크롤이 맨 아래가 아닌 경우 프리뷰 표시
}
if (ref.scrollTop > -10) {
// 스크롤이 맨 아래인 경우 프리뷰 제거
}
}, 200); // 0.2초 동안 스크롤 이벤트가 발생하지 않아야 handleScroll 함수 실행
return (
<div ref={chatsRef}>
</div>
);
사실 delay를 조금 더 줄까 생각도 해봤지만, 프리뷰가 보여지는데 문제가 있을 것 같아서 0.2초로 설정했다. 이벤트 핸들링 어렵지 않아요~~!!