Reputation: 4506
I'm trying to control the visibility of a React Component based on whether an individual is scrolling down on the component. The visibility is passed into the Fade
element as the "in" property.
I've set up a listener using the UseEffect Hook, which adds the listener onMount. The actual onScroll function is supposed to update the scrollTop state (which is the current value of the height to the top of the page) and then the scrolling state (which compares the event's scroll to the top of the page with the previous state, and if the first is greater than the second, returns true).
However, for some reason the setScrollTop hook isn't working, and the scrolling state continues to stay at 0.
What am I doing wrong? Here's the full component:
export const Header = (props) => {
const classes = useStyles();
const [scrolling, setScrolling] = useState(false);
const [scrollTop, setScrollTop] = useState(0);
const onScroll = (e) => {
setScrollTop(e.target.documentElement.scrollTop);
setScrolling(e.target.documentElement.scrollTop > scrollTop);
}
useEffect(() => {
window.addEventListener('scroll', onScroll);
},[]);
useEffect(() => {
console.log(scrollTop);
}, [scrollTop])
return (
<Fade in={!scrolling}>
<AppBar className={classes.header} position="fixed">
....
Upvotes: 33
Views: 75508
Reputation: 711
Or you can use window.pageYOffset. It's a bit more understandable for me that way:
const [scrolling, setScrolling] = useState(false);
const [scrollTop, setScrollTop] = useState(0);
useEffect(() => {
function onScroll() {
let currentPosition = window.pageYOffset; // or use document.documentElement.scrollTop;
if (currentPosition > scrollTop) {
// downscroll code
setScrolling(false);
} else {
// upscroll code
setScrolling(true);
}
setScrollTop(currentPosition <= 0 ? 0 : currentPosition);
}
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [scrollTop]);
Upvotes: 9
Reputation: 10873
You're missing the dependencies in your hook. Try this:
useEffect(() => {
const onScroll = e => {
setScrollTop(e.target.documentElement.scrollTop);
setScrolling(e.target.documentElement.scrollTop > scrollTop);
};
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, [scrollTop]);
By moving onScroll
inside the useEffect
, you don't need to track it on the hook's dependencies, however since it uses scrollTop
from the component's scope, you'll need to add it.
Alternatively, if for some reason you don't want to move onScroll
definition inside the useEffect
, you'll need to wrap onScroll
in useCallback
and track it in useEffect
's dependency array.
In general I'd recommend adding react-hooks/exhaustive-deps
to your ESlint rules
Also it's a good idea to remove the event listener in cleanup function.
Upvotes: 51