Files
HealthLog/src/hooks/use-mounted.ts
T
Marc-André Bombeck a8149e0bdf fix(ux): honest deletes, stable hydration and tidy numbers after release review
Server and first client render now agree everywhere query state used to
leak into the chrome, checkboxes no longer nest inside buttons, delete
confirmations admit the undo they offer, raw step counts drop their
decimals, the truncated unit label wraps instead, streaks know their
singular, the medication header speaks the user's date format, a failed
coach turn keeps the user's message and says what went wrong, settings
status notes follow a language switch, the locale only flips once its
bundle actually loaded, and the last raw api fetches in hooks move onto
the shared client.
2026-06-11 03:30:18 +02:00

39 lines
1.3 KiB
TypeScript

"use client";
import { useSyncExternalStore } from "react";
// Stable no-op subscription — the "store" never changes; only the
// server/client snapshot split below does the work.
const emptySubscribe = () => () => {};
/**
* v1.16.4 — SSR/CSR-consistent mount flag for query-dependent chrome.
*
* React hydrates streamed Suspense boundaries late, so a client
* component can hydrate AFTER a TanStack query already resolved.
* Any first render that branches on query state (`isLoading`,
* `isAuthenticated`, cached data) then disagrees with the
* server-rendered HTML and React logs hydration error #418.
*
* `useMounted()` returns `false` during SSR AND during the hydration
* render (React always uses the server snapshot of a
* `useSyncExternalStore` while hydrating, regardless of when the
* boundary hydrates), then `true` from the first client re-render.
* Gate query-derived branches on it:
*
* const mounted = useMounted();
* if (!mounted || query.isLoading) return <Skeleton />;
*
* Unlike the classic `useEffect(() => setMounted(true), [])` pattern
* this needs no state setter inside an effect (the repo's
* `react-hooks/set-state-in-effect` rule rejects that) and stays a
* single re-render.
*/
export function useMounted(): boolean {
return useSyncExternalStore(
emptySubscribe,
() => true,
() => false,
);
}