현재 아래와 같이 스크롤을 감지 하여 아래로 스크롤 될때는 정보를 가리지 않게 네베게이션 바가 아래로 내려가고 스크롤이 올라갈때는 네비게이션 바가 올라오는 효과를 React로 구현하고 싶었다.

참고한 사이트
일단 스크롤 이벤트 마다 특정 함수가 실행 되어야 한다고 생각했다.
특정 이벤트마다 실행되는 함수를 정의할때는 useEffect hook 을 이용하면 된다고 생각하여 바로 시작했다.
useEffect(() => {
const handleScroll = () => {
console.log("scrolling"); // 스크롤이 감지되면 로깅!
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
스크롤 감지는 되었고 스크롤시 현재 스크롤 위치를 로깅하는 함수 바꾸어 주었다.
useEffect(() => {
const handleScroll = () => {
console.log(document.documentElement.scrollTop); // 뷰의 최상단 위치를 로깅!
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
위처럼 document.documentElement.scrollTop 를 확인하면 스크롤이 최상단에 위치할때 0 이 찍히는 것을 볼수 있다.
여기서 handleScroll 함수가 실행 됬을때 이전 스크롤과 현재 스크롤을 비교 했을때
으로 조건문 처리하여 상태를 저장해 보았다.
내려가는중인지 판단 하기 위해 isScrollDowning 상태와 이전 스크롤 위치를 저장할 lastTopScroll 상태를 선언하였다.
const [isScrollDowning, setIsScrollDowning] = useState(false);
const [lastTopScroll, setLastTopScroll] = useState(
document.documentElement.scrollTop
);
useEffect(() => {
const handleScroll = () => {
const currentTopScroll = document.documentElement.scrollTop;
if (currentTopScroll > lastTopScroll) {
setIsScrollDowning(true);
} else {
setIsScrollDowning(false);
}
setLastTopScroll(currentTopScroll);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
위 코드는 문제가 있다.
바로 lastTopScroll 상태를 useEffect 에서 사용하면서 의존성 배열에 넣지 않았기 때문인데
내가 원하던 동작은 스크롤 이벤트가 감지됐을 때 이전 스크롤 위치와 현재 스크롤 위치를 비교하여 동작하는 것인데 위 lastTopScroll 는 클로저에 갇혀 버려 더이상 업데이트 되지 않는 상수 처럼 취급 되기 때문이다.
handleScroll에서 `console.log(lastTopScroll);` 를 찍어보면 간단히 확인 가능하다.

아무리 스크롤해도 바뀌지 않는 모습…
간단하다 아래와 같이 의존성 배열에 useEffect 안에서 사용중인lastTopScroll 상태를 추가 해주자
클로저로 lastTopScroll 변수가 갇히지만 변할때마다 다시 함수를 만들어 준다면 클로저에 갇혀도 상관없을 것이다.
const [isScrollDowning, setIsScrollDowning] = useState(false);
const [lastTopScroll, setLastTopScroll] = useState(
document.documentElement.scrollTop
);
useEffect(() => {
...
return () => window.removeEventListener("scroll", handleScroll);
}, [lastTopScroll]); // 여기 추가!
만약 useEffect의 return 에 (언마운트시 동작할 함수) 이벤트 리스너를 제거하지 않으면 lastTopScroll 이 변경되며 수많은 리렌더링시 이벤트 리스너가 너무 많아 성능이 저하될 것이다!
offset 으로 자잘한 감지 막기
현재 구현한 기능은 자잘한 스크롤 (1px) 만이라도 움직이면 상태가 변경되어 navbar가 올라갔다 내려갔다 한다…
내가 참고한 사이트 에서는 이렇게 동작 하지 않았다…
offset 변수를 설정하여 특정 움직이 이하일 때는 감지를 막아보자!
사실 이건 정말 간단하다 offset 상수를 설정하고 if문으로 early return 으로 처리하면 되기 때문이다
useEffect(() => {
const handleScroll = () => {
const currentTopScroll = document.documentElement.scrollTop;
// offset 만큼의 변화가 없다면 무시!
if (Math.abs(currentTopScroll - lastTopScroll) < offset) return;
if (currentTopScroll > lastTopScroll) {
setIsScrollDowning(true);
} else {
setIsScrollDowning(false);
}
setLastTopScroll(currentTopScroll);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, [lastTopScroll]);

이제 참고한 사이트와 똑같이 기능이 동작한다!