fix(home): onboarding slot uses a module-level lazy (no component chosen during render); warm-snapshot tests load the city table like the boot does

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4f2nya58RoRu4yEmJgRGE
This commit is contained in:
Jesse_Chen
2026-10-01 00:45:30 +08:00
co-authored by Claude Opus 5.5
parent 31d0737897
commit e2c3791fad
3 changed files with 69 additions and 11 deletions
@@ -1,22 +1,38 @@
"use client";
import dynamic from "next/dynamic";
import { lazy, Suspense } from "react";
import type { HomeOnboardingShellProps } from "@/components/home-onboarding-shell";
import type { HomeOnboardingShell, HomeOnboardingShellProps } from "@/components/home-onboarding-shell";
import { loadedHomeOnboardingShell, loadHomeOnboardingShell } from "@/lib/home-onboarding-shell-loader";
// Only reached when the preload below did not finish (or failed) before the
// shell had to paint; renders nothing until the chunk arrives, never a spinner.
const DynamicHomeOnboardingShell = dynamic(loadHomeOnboardingShell, { loading: () => null });
type ShellModule = { default: typeof HomeOnboardingShell };
/**
* The onboarding shell (birth-place picker, China city table, birth-time
* intake) is only for people whose profile is incomplete, so it left the home
* first-screen bundle (TASK-home-first-load T7, BUG-1130). The cold boot
* preloads it before reveal when the profile is incomplete, so this renders the
* loaded component directly and the reveal shows it in the same frame.
* preloads it before reveal when the profile is incomplete; `lazy` is then
* handed an already-settled thenable, which React resolves synchronously, so
* the reveal shows the shell in the same frame. Without the preload it loads
* behind an empty fallback — never a spinner.
*/
const LazyHomeOnboardingShell = lazy((): Promise<ShellModule> => {
const loaded = loadedHomeOnboardingShell();
if (loaded) {
// A thenable that calls back synchronously: React marks the lazy resolved
// before it would suspend.
const settled: PromiseLike<ShellModule> = {
then: (resolve) => Promise.resolve(resolve ? resolve({ default: loaded }) : { default: loaded }) as never,
};
return settled as Promise<ShellModule>;
}
return loadHomeOnboardingShell().then((Shell) => ({ default: Shell }));
});
export function HomeOnboardingShellSlot(props: HomeOnboardingShellProps) {
const Loaded = loadedHomeOnboardingShell();
return Loaded ? <Loaded {...props} /> : <DynamicHomeOnboardingShell {...props} />;
return (
<Suspense fallback={null}>
<LazyHomeOnboardingShell {...props} />
</Suspense>
);
}