홈 페이지 하단 영역이 길어지는 만큼 사용자 경험 측면을 고려해서 Top 버튼
이 필요했다.
const ScrollTop = () => {
const [showButton, setShowButton] = useState(false);
const clickScrollTopHandler = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};
useEffect(() => {
const clickShowButtonHandler = () => {
if (window.scrollY > 700) {
setShowButton(true);
} else {
setShowButton(false);
}
};
window.addEventListener('scroll', clickShowButtonHandler);
return () => {
window.removeEventListener('scroll', clickShowButtonHandler);
};
}, []);
return (
<>
{showButton && (
<div className={styles.scrollTopBox}>
<button onClick={clickScrollTopHandler}>
<p>Top</p>
</button>
</div>
)}
</>
);
};
먼저 show/hide 처리를 하기 위해 useState로 버튼 상태 관리를 진행했고 window 내장 API인 scrollTo로 클릭 핸들러를 생성해서 페이지 상단으로 부드럽게 스크롤 되도록 옵션을 부여했다.
그리고 useEffect를 사용해서 마운트 될 때 scroll 이벤트 리스너로 clickShowButtonHandler 함수를 콜백했고 언마운트 시, scroll 이벤트를 정리했다.
window.scrollY > 700 조건은 스크롤 위치가 700px 이상일 때를 의미한다.
단, 리렌더링 여부를 파악하기 위해 console.log를 출력한 결과...
상당히 마음이 불편해진 결과를 볼 수 있었다.
clickShowButtonHandler 함수가 스크롤 이벤트가 발생할 때 마다 호출되고 있어서 성능적인 측면에서 굉장히 떨어졌고 이벤트 핸들러 내에서 사용되는 조건문을 최적화 하기 위해서 useCallback
훅을 적용해서 clickScrollTopHandler와 clickShowButtonHandler 함수가 필요할 때만 재생성되도록 했다.
const ScrollTop = () => {
const [showButton, setShowButton] = useState(false);
const clickScrollTopHandler = useCallback(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, []);
const clickShowButtonHandler = useCallback(() => {
setShowButton(window.scrollY > 700);
}, []);
useEffect(() => {
window.addEventListener('scroll', clickShowButtonHandler);
return () => {
window.removeEventListener('scroll', clickShowButtonHandler);
};
}, [clickShowButtonHandler]);
return (
showButton && (
<div className={styles.scrollTopBox}>
<button onClick={clickScrollTopHandler}>Top</button>
</div>
)
);
};
적용 시킨 코드 결과이다.
clickShowButtonHandler을 이벤트 핸들러 내부에서 setShowButton을 직접 호출하도록 해서 불필요한 조건문을 줄였다.
더불어 useCallback을 적용시킴으로써 위 작업한 컴포넌트가 리렌더링될 때마다 불필요한 함수 생성을 방지해서 메모리 사용량을 줄이고 성능을 향상 시킨 결과를 얻었다.
더 이상 console.log로 인한 마음이 어려워지는 현상은 가셨다.
패키지의 맞게 설치하면 된다.
npm install @types/lodash
yarn add @types/lodash
debounce 함수는 연속된 호출을 그룹화하여 단 하나의 함수 호출만 실행되도록 지연시키는 기법
import { debounce } from 'lodash';
const clickShowButtonHandler = debounce(() => {
setShowButton(window.scrollY > 700);
}, 100);
useEffect(() => {
...
return () => {
window.removeEventListener('scroll', clickShowButtonHandler);
clickShowButtonHandler.cancel();
};
}, [clickShowButtonHandler]);
굉장히 간단하게도 debounce를 import 후 호출하고 setShowButton을 감싸주면 된다.
이렇게되면 빈번하게 일어나는 이벤트의 발생을 줄여주는 효과를 볼 수 있다. 난 100밀리초로 해놔서 1초마다 지연시켜 호출시켰다.
쉽게 이해를 돕자면 1초 마다 지연 시키면서 setShowButton 함수가 호출되면서 showButton 상태를 업데이트한다. (스크롤이 멈춘 후에 함수를 실행하도록 하는 것이다.)
그리고 컴포넌트가 언마운트될 때 마다 debounce에 의해 예약된 호출을 취소하는 cancel 메서드를 호출하는 것도 중요하다.