Skip to content

Commit 016f355

Browse files
committed
fix: hydration-safe initialization for useOverlays and useCollapsible
1 parent b6e58e7 commit 016f355

2 files changed

Lines changed: 18 additions & 15 deletions

File tree

showcase/shell-dashboard/src/components/collapsible-category.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Collapse state is persisted in localStorage using the key
77
* `dashboard-collapse-{name}`.
88
*/
9-
import { useState } from "react";
9+
import { useState, useEffect } from "react";
1010

1111
/* ------------------------------------------------------------------ */
1212
/* localStorage helpers */
@@ -55,7 +55,11 @@ export function useCollapsible({
5555
name,
5656
defaultOpen,
5757
}: UseCollapsibleOptions): UseCollapsibleReturn {
58-
const [isOpen, setIsOpen] = useState(() => readStorage(name, defaultOpen));
58+
const [isOpen, setIsOpen] = useState(defaultOpen);
59+
60+
useEffect(() => {
61+
setIsOpen(readStorage(name, defaultOpen));
62+
}, [name, defaultOpen]);
5963

6064
const toggle = () => {
6165
const next = !isOpen;

showcase/shell-dashboard/src/hooks/useOverlays.ts

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,20 +126,19 @@ export interface UseOverlaysReturn {
126126
export function useOverlays(): UseOverlaysReturn {
127127
const initialized = useRef(false);
128128

129-
const [overlays, setOverlays] = useState<OverlaySet>(() => {
130-
const { overlays: fromHash } = parseHash();
131-
if (fromHash) return fromHash;
132-
133-
const fromStorage = loadFromStorage();
134-
if (fromStorage) return fromStorage;
135-
136-
return new Set(DEFAULT_OVERLAYS) as OverlaySet;
137-
});
129+
const [overlays, setOverlays] = useState<OverlaySet>(
130+
() => new Set(DEFAULT_OVERLAYS) as OverlaySet,
131+
);
132+
const [activeTab, setActiveTabRaw] = useState<"matrix" | "ops">("matrix");
138133

139-
const [activeTab, setActiveTabRaw] = useState<"matrix" | "ops">(() => {
140-
const { tab } = parseHash();
141-
return tab;
142-
});
134+
// Sync from URL hash / localStorage after hydration
135+
useEffect(() => {
136+
const { tab, overlays: fromHash } = parseHash();
137+
const resolved =
138+
fromHash ?? loadFromStorage() ?? (new Set(DEFAULT_OVERLAYS) as OverlaySet);
139+
setOverlays(resolved);
140+
setActiveTabRaw(tab);
141+
}, []);
143142

144143
// On mount, write hash to reflect actual state (handles legacy redirects
145144
// and fallback from localStorage where the URL had no hash).

0 commit comments

Comments
 (0)