1. scroll 이벤트와 window.scrollY
window에는 ‘scroll’이라는 이벤트가 존재한다.
이 두가지를 활용할 것이다.
2. scroll 계산하기
로직은 다음과 같다. scrollY값을 상태값으로 저장한다. 그리고, window.addEventListenr(’scroll’)에, scrollY값을 구하여 이전에 저장한 scrollY값보다 크다면 스크롤이 내려간 것이고, 아니라면 올라간 것으로 판단한다.
const [scrollY,setScrollX] = useState(0);
useEffect(()=>{
const handleScroll = () => {
const currentScrollY = window.scrollY;
if(currentScrollY>scrollY){
// 스크롤이 내려간다 = Header를 숨긴다.
}else{
// 스크롤이 올라간다 = Header를 보여준다.
}
setScrollY(currentScrollY);
}
window.addEventListner('scroll',handleScroll);
return () => window.removeEventListener('scroll',handleScroll);
},[scrollY])
3. 추상화하기
이 scroll을 계산하는 과정을 추상화해보자.
useScroll훅을 사용할 때, scrollY값은 항상 필요할테니 return하도록 한다. 그리고, scroll이 변경되었을 때 추가 동작을 위해 callbackFn을 인수로 전달받는다.
// useScroll.ts
export default function useScroll({ callbackFn }: UseScrollProps = {}) {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = () => {
setScrollY(window.scrollY);
callbackFn?.();
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, [scrollY]);
return {
scrollY,
};
}
// HeaderWrapper.tsx
const { scrollY: prevScrollY } = useScroll({
callbackFn: () => {
const currentScrollY = window.scrollY;
setIsChildrenShow(prevScrollY > currentScrollY);
},
});
4. TMI
현재, Header는 서버에서만 렌더링하기 위해 RSC 컴포넌트를 유지하고 있다. RSC에서는 클라이언트에서만 동작하는 리액트 훅을 사용하지 못한다. 그래서, Header외에 HeaderWrapper 컴포넌트를 만들었다. 구조는 다음과 같다.
// layout.tsx
return (
<HeaderWrapper> // RCC
<Header /> // RSC
// ..
</HeaderWrapper>
)