Here:
const useHideOnScroll = () => {
const prevScrollY = React.useRef<number>();
const [isHidden, setIsHidden] = React.useState(false);
React.useEffect(() => {
const onScroll = () => {
setIsHidden(isHidden => {
const scrolledDown = window.scrollY > prevScrollY.current!;
if (scrolledDown && !isHidden) {
return true;
} else if (!scrolledDown && isHidden) {
return false;
} else {
prevScrollY.current = window.scrollY;
return isHidden;
}
});
};
console.log("adding listener");
window.addEventListener("scroll", onScroll);
return () => {
window.removeEventListener("scroll", onScroll);
};
}, []);
return isHidden;
};
const Navbar = () => {
const isHidden = useHideOnScroll();
console.info("rerender");
return isHidden ? null : <div className="navbar">navbar</div>;
};
export default Navbar;
You might have concern about setIsHidden
causing rerender on every onScroll
, by always returning some new state value, but a setter from useState
is smart enough to update only if the value has actually changed.
Also your .navbar
(I've added a class to it) shouldn't change the layout when it appears or your snippet will get locked in an infinite loop. Here're appropriate styles for it as well:
.navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 30px;
background: rgba(255, 255, 255, 0.8);
}
Full CodeSandbox: https://codesandbox.io/s/13kr4xqrwq