How do I wait for scroll to be restored?
Answered
Hairy Woodpecker posted this in #help-forum
Hairy WoodpeckerOP
I've been tasked with added a fancy effect to a nav bar so it's different at the top of certain pages. I need to track if the page is scrolled, which I monitor with a setInterval. However, I get flickering because the scroll jumps to 0 and is then restored during navigation.
I need to know in my setInterval when a) nativagation is in progress and/or b) when scroll has been restored.
I need to know in my setInterval when a) nativagation is in progress and/or b) when scroll has been restored.
Answered by Palomino
Use scroll events + debounce instead of setInterval, and tie it to useRouterState in a useEffect so it re-runs when navigation finishes.
6 Replies
@Hairy Woodpecker I've been tasked with added a fancy effect to a nav bar so it's different at the top of certain pages. I need to track if the page is scrolled, which I monitor with a setInterval. However, I get flickering because the scroll jumps to 0 and is then restored during navigation.
I need to know in my setInterval when a) nativagation is in progress and/or b) when scroll has been restored.
Palomino
Use scroll events + debounce instead of setInterval, and tie it to useRouterState in a useEffect so it re-runs when navigation finishes.
Answer
Hairy WoodpeckerOP
I've got something working but still with intervals:
import { usePathname } from "next/navigation";
import { useEffect, useRef, useState } from "react";
export const useIsScrolled = (): boolean => {
const pathname = usePathname();
const prevPathname = useRef(pathname);
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
let update: (timestamp: number) => void;
const updateIsScrolled = () => {
setIsScrolled(window.scrollY !== 0);
};
if (pathname === prevPathname.current) {
update = updateIsScrolled;
} else {
prevPathname.current = pathname;
let prevY = window.scrollY;
let changedAt = -Infinity;
update = (timestamp: number) => {
const y = window.scrollY;
if (y === prevY) {
if (timestamp - changedAt > 100) {
update = updateIsScrolled;
}
} else {
prevY = y;
changedAt = timestamp;
}
};
}
let rafId: number | undefined;
const handleAnimationFrame = (timestamp: number) => {
rafId = undefined;
update(timestamp);
rafId = requestAnimationFrame(handleAnimationFrame);
};
rafId = requestAnimationFrame(handleAnimationFrame);
return () => {
if (rafId !== undefined) {
cancelAnimationFrame(rafId);
rafId = undefined;
}
};
}, [pathname]);
return isScrolled;
};Do you think there's much point which to scroll events?
I think I've done what you're suggesting except with animation frames instead of scroll events.
Palomino
rAF is fine, scroll events aren’t worth switching to here.
Hairy WoodpeckerOP
Thanks for your help 🙂