refactor(ui): simplify useBreakpoint singleton store

Initialize listeners as an eager Set to eliminate the undefined union
type and non-null assertions. Add comment explaining the change listener
algorithm.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
This commit is contained in:
Johan Persson
2026-02-27 09:46:18 +01:00
parent cd3cb0f35a
commit f10b3b8d7d
+12 -10
View File
@@ -50,10 +50,11 @@ function computeBreakpoint(): Breakpoint {
}
// --- Singleton store ---
// Created lazily on first subscribe/getSnapshot call.
// `current` is initialized lazily on first client-side access.
// `listeners` is eagerly initialized since Set is safe to create on the server.
let current: Breakpoint | undefined;
let listeners: Set<() => void> | undefined;
const listeners = new Set<() => void>();
let initialized = false;
function ensureInitialized(): void {
@@ -62,34 +63,35 @@ function ensureInitialized(): void {
}
initialized = true;
current = computeBreakpoint();
listeners = new Set();
// Register one listener per breakpoint. When any query fires, re-evaluate
// all breakpoints to find the new active one. Notify subscribers only if
// the active breakpoint actually changed.
for (const bp of breakpoints) {
const mql = window.matchMedia(`(min-width: ${bp.value}px)`);
const onChange = () => {
mql.addEventListener('change', () => {
const next = computeBreakpoint();
if (next !== current) {
current = next;
for (const cb of listeners!) {
for (const cb of listeners) {
cb();
}
}
};
mql.addEventListener('change', onChange);
});
}
}
function subscribe(callback: () => void): () => void {
ensureInitialized();
listeners!.add(callback);
listeners.add(callback);
return () => {
listeners!.delete(callback);
listeners.delete(callback);
};
}
function getSnapshot(): Breakpoint {
ensureInitialized();
return current!;
return current ?? 'initial';
}
function getServerSnapshot(): Breakpoint {