Reputation: 2216
I am trying to show a specific component - a "sticky header" piece when the page displaying it, is scrolled X number of points off the top.
I tried doing this with the useWindowScroll
hook from the use-react
package, as well with a short fragment below:
const [scrollTop, setScrollTop] = useState(0);
function setScroll() {
console.log('setScroll');
setScrollTop(window.pageYOffset);
console.log(new Date().getTime());
}
useEffect(() => {
function watchScroll() {
console.log('watchScroll');
window.addEventListener("scroll", setScroll);
}
watchScroll();
return () => {
window.removeEventListener("scroll", setScroll);
};
});
console.log('scroll top:', scrollTop);
Ideally, we probably want to have a bit of a delay before each new scroll event is processed, yet it is not my problem.
I would expect setScroll
to get called whenever I scroll, every however many millisecond. However, this is NOT happening. In fact, I don't see it called ever.
Since it is not being called when approriate, my const shouldShowStickyHeader = scrollTop > 1000;
is always set to false
. Hence, my is never shown. I want it to appear when the scroll to top distance is 1,000.
What is the problem?
I also tried version with everything inside the effects hook as:
const [scrollTop, setScrollTop] = useState(0);
useEffect(() => {
function setScroll() {
console.log('setScroll');
setScrollTop(window.pageYOffset);
console.log(new Date().getTime());
}
window.addEventListener("scroll", setScroll);
return () => {
window.removeEventListener("scroll", setScroll);
};
}, [scrollTop]);
I don't see setScroll
being printed in the console. What could be the problem?
I did a little test with document.querySelectorAll("*").forEach(element => element.addEventListener("scroll", ({target}) => console.log(target, target?.id, target?.parent, target?.parent?.id)));
. There are indeed events being fired off when I scroll. See:
<div data-testid="vdp-scroll-container" id="vdp-scroll-container" class="jsx-1959376998 vdp-scroll-container">
...
<div data-testid="vdp-scroll-container" id="vdp-scroll-container" class="jsx-1959376998 vdp-scroll-container">
I tried putting listers directly on this element with:
document.getElementById('#vdp-scroll-container')?.addEventListener("scroll", setScroll);
return () => {
document.getElementById('#vdp-scroll-container')?.removeEventListener("scroll", setScroll);
};
Still, I don't see anything getting fired off.
The styles on this <div/>
are below:
height: 100%;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
overflow-y: scroll;
Am I not getting anything because of that?
Upvotes: 3
Views: 7916
Reputation: 2216
Attaching the listener to window did not work. However, after I removed height: 100%
, I was able to subscribe to the scroll event with:
const [scrollTop, setScrollTop] = useState(0);
useEffect(() => {
const setScroll = () => {
setScrollTop(window.pageYOffset);
};
document.querySelector('#vdp-scroll-container')?.addEventListener("scroll", setScroll);
return () => {
document.querySelector('#vdp-scroll-container')?.removeEventListener("scroll", setScroll);
};
}, [scrollTop]);
Upvotes: 3
Reputation: 1567
You should pass an empty array to useEffect as the second argument so that the function is fired only after the component mounts for the first time. Not passing a second argument, will trigger the function after every rerender and passing [scrollTop]
will trigger the function every time scrollTop
changes.
Something like this should work:
const [offset, setOffset] = React.useState(null);
const setScroll = () => {
setOffset(window.scrollY);
};
React.useEffect(() => {
window.addEventListener("scroll", setScroll);
return () => {
window.removeEventListener("scroll", setScroll);
};
}, []);
Working example: https://codesandbox.io/s/zen-cdn-ll6es?file=/src/App.js
Upvotes: 3