Skip to content

Commit 6d456fa

Browse files
committed
Fix nav hydration mismatch to prevent sidebar flash on reload
1 parent b65ea6c commit 6d456fa

1 file changed

Lines changed: 36 additions & 26 deletions

File tree

src/components/Nav/NavPanels.tsx

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ import { JumpToLinks } from "./JumpToLinks";
33
import { MainNavLinks } from "./MainNavLinks";
44
import { useEffect, useState } from "preact/hooks";
55

6+
const BREAKPOINT = 768;
7+
8+
const getIsMobile = () => {
9+
if (typeof window === "undefined") return false; // assume desktop on SSR
10+
return window.innerWidth < BREAKPOINT;
11+
};
12+
13+
614
interface NavPanelsProps {
715
mainLinks: {
816
label: string;
@@ -32,38 +40,40 @@ export const NavPanels = (props: NavPanelsProps) => {
3240
jumpToState,
3341
} = props;
3442

35-
const [isOpen, setIsOpen] = useState({ main: false, jump: false });
36-
const [isMobile, setIsMobile] = useState(true);
43+
const [isMobile, setIsMobile] = useState(getIsMobile);
44+
45+
const [isOpen, setIsOpen] = useState(() => {
46+
const mobile = getIsMobile();
47+
return {
48+
main: !mobile,
49+
jump: !mobile,
50+
};
51+
});
52+
3753

3854
// Defaults to closed on mobile, open on desktop
3955
// Have to do this in a lifecycle method
4056
// so that we can still server-side render
4157
useEffect(() => {
42-
const startsMobile = window.innerWidth < 768;
43-
setIsMobile(startsMobile);
44-
setIsOpen({ main: !startsMobile, jump: !startsMobile });
45-
// We use a resize observer to the user's window crosses the
46-
// threshhold between mobile and desktop
47-
const documentObserver = new ResizeObserver((entries) => {
48-
for (const entry of entries) {
49-
if (!isMobile && entry.contentRect.width < 768) {
50-
setIsMobile(true);
51-
setIsOpen({
52-
main: false,
53-
jump: false,
54-
});
55-
} else if (isMobile && entry.contentRect.width >= 768) {
56-
setIsMobile(false);
57-
setIsOpen({
58-
main: true,
59-
jump: true,
60-
});
61-
}
62-
}
58+
const handleResize = () => {
59+
const mobile = window.innerWidth < BREAKPOINT;
60+
61+
setIsMobile((prev) => {
62+
if (prev === mobile) return prev;
63+
64+
setIsOpen({
65+
main: !mobile,
66+
jump: !mobile,
67+
});
68+
69+
return mobile;
6370
});
64-
documentObserver.observe(document.body);
65-
return () => documentObserver.disconnect();
66-
}, [setIsMobile, setIsOpen, isMobile]);
71+
};
72+
73+
window.addEventListener("resize", handleResize);
74+
return () => window.removeEventListener("resize", handleResize);
75+
}, []);
76+
6777

6878
const handleMainNavToggle = () => {
6979
setIsOpen((prev) => ({

0 commit comments

Comments
 (0)