Skip to content

Latest commit

 

History

History
71 lines (47 loc) · 5.45 KB

File metadata and controls

71 lines (47 loc) · 5.45 KB

Fix: Loader Blocks UI Until 3D Model is Ready

Problem Diagnosis

There are 3 root causes causing the UI to flash before/during the loader:

Bug 1 — Hydration gap (most visible flash)

LoadingWrapper uses isHydrated state (starts false). Until React hydrates, shouldShowInitialLoading = false, so the loader is invisible for ~200–500ms while the page renders below it.

isHydrated: false  →  shouldShowInitialLoading = false  →  loader not rendered
React hydrates   →  isHydrated = true  →  loader NOW appears  ← UI already visible

Bug 2 — Loader completes on video/timeout, not on model load

onComplete in LoadingWrapper calls setVideoComplete(true) when the video ends or the fallback timeout fires — but onModelLoaded in Hero.tsx never connects to the loading context. The loader can disappear before Charizard is ready.

Bug 3 — Children rendered unconditionally

LoadingWrapper always renders {children} first (intentional, for 3D preloading), then overlays the loader. But because of Bug 1, there's a window where children are visible and the loader isn't mounted yet.


Proposed Changes

Component: LoadingWrapper.tsx

[MODIFY] LoadingWrapper.tsx

  • Remove the isHydrated guard — instead render the loader overlay with opacity: 0 → 1 CSS immediately on mount (avoids hydration mismatch and the flash).
  • Add suppressHydrationWarning on the loader div so SSR/client mismatch is silently accepted.
  • Replace handleVideoComplete → setVideoComplete with finishInitialLoading — the loader should complete when the model is loaded, not when the video ends.
  • Expose a onModelReady callback prop that Hero.tsx can call when Charizard finishes loading.
  • Wire it into LoadingContext via finishInitialLoading.

Component: LoadingContext.tsx

[MODIFY] LoadingContext.tsx

  • Merge isVideoComplete logic into isModelLoaded: the loader should finish when setModelLoaded(true) is called.
  • Keep isVideoComplete state so nothing else breaks.
  • Add a safety timeout (6 s) — if the model never fires onLoaded, dismiss the loader anyway (prevents infinite load on WebGL errors).

Component: Hero.tsx

[MODIFY] Hero.tsx

  • Call setModelLoaded(true) via useLoading() inside the existing onModelLoaded callback — one line added.
  • This directly connects model-ready signal to the loading context.

Component: LoadingScreen.tsx

[MODIFY] LoadingScreen.tsx

  • No structural changes needed — just update the fallbackTimeoutMs default to 6000 to align with context safety timeout (was 3000, which could dismiss the loader before the model is ready on slow connections).

Verification Plan

TypeScript Check (automated)

cd "c:\N Drive\Github projects\portfolio-charizardop" && npx tsc --noEmit

Expected: exit code 0, no errors.

Manual Visual Test

  1. Run npm run dev and open http://localhost:3000 in an incognito window (clears sessionStorage so the "starter" loader path is triggered).
  2. Expected: Loader appears instantly — no flash of page content underneath.
  3. Expected: The loader stays up until Charizard finishes flying into frame.
  4. Expected: After ~1–2 s of Charizard being visible, loader fades out smoothly.
  5. Reload the page (non-incognito, hasVisited set) — the returning-visitor fast loader (~1.5 s) should show immediately with no flash.