PURPOSE

Dev-only React component that intercepts Vite HMR-triggered full-page reloads in the playground screen. Instead of letting the page silently refresh, it surfaces a clickable badge so the developer chooses when to reload, preserving in-memory playground state across HMR cycles.

OWNS

  • A module-level reload-block state (_originalReload, _blocked) that survives HMR re-renders of the component itself.
  • The override of Location.prototype.reload for the lifetime of the component (installed on mount, removed on unmount).
  • A pending local React state flag indicating that a reload was attempted and is being held.
  • The BADGE style object and the floating amber button rendered in the top-right corner when a reload is pending.

READS FROM

  • import.meta.env.DEV — gates the entire behavior to development builds; production renders nothing and never installs the override.
  • import.meta.hot — when available, subscribes to Vite’s vite:beforeFullReload event as a secondary trigger.
  • Location.prototype.reload — read once on install to capture the original reload function for later restoration and explicit invocation.

PUSHES TO

  • Location.prototype — replaces reload with a guarded version via Object.defineProperty and restores the original on cleanup.
  • window — dispatches a pg:reload-blocked CustomEvent whenever a reload call is intercepted while the block is active.
  • Local React state — flips pending to true when either the custom event fires or Vite’s vite:beforeFullReload hook fires.
  • window.location (via the captured original reload) when the developer clicks the badge to actually perform the reload.

DOES NOT

  • Does not affect production builds; both the effect and the render bail out when import.meta.env.DEV is false.
  • Does not block reloads triggered outside location.reload() (e.g., explicit location.href assignment, anchor navigation, browser refresh button).
  • Does not persist pending across remounts or page loads; it is purely in-memory React state.
  • Does not coordinate with other guards or screens; the override is global to the page once installed, but the badge UI is local to this component.
  • Does not log, telemeter, or otherwise report blocked reloads.

Signals

  • Event listened to: pg:reload-blocked on window.
  • Event emitted: pg:reload-blocked on window (via CustomEvent), dispatched from inside the patched reload.
  • Vite HMR hook: vite:beforeFullReload via import.meta.hot.on.
  • Return value of installReloadBlock indicates whether the browser permitted the prototype override; failure is swallowed and the component renders nothing.

Entry points

  • PlaygroundUpdateGuard — named export, the React component. Mounted once inside the playground screen tree.
  • installReloadBlock / removeReloadBlock — internal helpers; not exported.

Pattern notes

  • Module-level (not component-level) state is used so HMR replacing the component module does not lose the override or leak duplicate patches.
  • Idempotent install: installReloadBlock early-returns when _blocked is already set.
  • Belt-and-suspenders: both the prototype override and the Vite vite:beforeFullReload hook can independently set pending, covering paths where Vite reloads via mechanisms other than location.reload().
  • Cleanup restores the original reload and removes the listener, so unmounting the component returns the page to default browser behavior.
  • The override uses Object.defineProperty with configurable: true so a subsequent restore (or another patch) can replace it cleanly.
  • Failure to override (some browser sandboxes refuse) is silently tolerated — the component simply renders nothing.