skip to Main Content

Description

I’m encountering an issue with the header component in my React based next.js web application. Upon the initial load of the page, when I first scroll, there’s a noticeable jittery behavior before the header hides as intended. Once this initial jittery behavior occurs, the header behaves perfectly fine for subsequent scrolls.

function Header() {
    const [mounted, setMounted] = useState(false);
    const { theme } = useTheme();
    const [prevScrollPos, setPrevScrollPos] = useState(0);
    const [visible, setVisible] = useState(true);

    useEffect(() => {
        const handleScroll = () => {
            const currentScrollPos = window.scrollY;
            setVisible(prevScrollPos > currentScrollPos || currentScrollPos < 200);
            setPrevScrollPos(currentScrollPos);
        };

        window.addEventListener("scroll", handleScroll);

        return () => window.removeEventListener("scroll", handleScroll);
    }, [prevScrollPos]);

    useEffect(() => {
        setMounted(true);
    }, []);

    if (!mounted) {
        return null;
    }

    return (
        <>
            {(theme === 'light' || theme === 'undefined' || theme === null) && visible && <LightHeader />}
            {theme === 'dark' && visible && <DarkHeader />}
        </>
    )
}

(I am using next-themes for theme switching. The mounting logic is to mitigate hydration error)

I suspect I might be missing a crucial React concept or best practice here. Could someone please provide insights into what might be causing this jittery behavior on the first scroll and how I can resolve it? Would de-bouncing help in this scenario? Additionally, could this issue be related to React Snapshot?

Any help or guidance would be greatly appreciated! Thank you.


warning suggests that I read scroll anchoring

2

Answers


  1. Chosen as BEST ANSWER

    The issue was with scroll not able to anchor due to dynamically adding and removing the header component. I was rendering either the header component or null which was affecting the height of the document.

    I mitigated this issue by using visibility: visible and hidden such that it is still in the document flow but hidden.

    NOTE: I still don't know why the issue resolves itself after initial jitter tho. Is it because the height of the header is saved by the document?


  2. I haven’t reproduced the problem yet. But these two things might help.

    First, add visible to the useEffect’s dependency list(I’m sure there’s a warning by the linter):

            useEffect(() => {
            const handleScroll = () => {
                const currentScrollPos = window.scrollY;
                setVisible(prevScrollPos > currentScrollPos || currentScrollPos < 200);
                setPrevScrollPos(currentScrollPos);
            };
    
            window.addEventListener("scroll", handleScroll);
    
            return () => window.removeEventListener("scroll", handleScroll);
        }, [prevScrollPos, visible]);
    

    Second, change useState to useRef for the mounted variable:

          const mounted = useRef(false);
        
          useEffect(() => {
            mounted.current = true;
            return () => {
              mounted.current = false;
            };
          }, []);
        
          if (!mounted.current) {
            return null;
          }
    

    Using useRef avoids unnecessary renders.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search