UNPKG

@mui/internal-docs-infra

Version:

MUI Infra - internal documentation creation tools.

80 lines (75 loc) 3.24 kB
'use client'; import * as React from 'react'; import { CoordinatedContentContext } from "./CoordinatedContentContext.mjs"; import { useSettleGate } from "../useCoordinated/useSettleGate.mjs"; import { pageSettleGate } from "../useCoordinated/pageSettleGate.mjs"; /** * Lazily import a component and render it once its chunk has loaded, reporting * readiness to the settle gate - so the page can coordinate the swap and a * `StreamController` can reflect it in `loading`. * * The import runs in an effect (not `React.lazy` + Suspense) on purpose: the swap * that reveals this content mounts/unmounts the subtree around a pending `import()`, * and a Suspense boundary that comes and goes around a pending promise trips React's * async-info-on-boundary tracking ("cleaning up async info that was not on the * parent Suspense boundary"). Loading in an effect avoids a Suspense boundary * entirely, and renders only the fallback during SSR (effects don't run there). * * The import factory is captured once (via lazy `useState`), so an inline * `content={() => import('./X')}` doesn't restart the import every render. While * the module loads, the explicit `fallback` (or, if none, the coordinating swap's * fallback from {@link CoordinatedContentContext}) is shown - so the same * placeholder keeps covering the load, with no empty flash. */ import { jsx as _jsx } from "react/jsx-runtime"; export function LazyContent({ content, props, fallback = null, gate }) { // Capture the import thunk once - consumers pass an inline thunk (new identity // each render), and we must not restart the import on every render. const [importContent] = React.useState(() => content); const [Loaded, setLoaded] = React.useState(null); const coordinated = React.useContext(CoordinatedContentContext); const reportReady = coordinated.reportReady; React.useEffect(() => { let active = true; importContent().then(mod => { if (active) { // Function form: the stored value is itself a component (a function), // so a bare `setLoaded(mod.default)` would be treated as an updater. setLoaded(() => mod.default); } }).catch(() => { // Leave the fallback in place on a failed import (network error, missing // chunk); a parent error boundary can handle a hard failure if needed. }); return () => { active = false; }; }, [importContent]); // Signal the coordinating swap (and settle gate) that the content has loaded. const loaded = Loaded !== null; React.useEffect(() => { if (loaded) { reportReady?.(); } }, [loaded, reportReady]); // Pending until the module loads; report to the page-global gate and, when // given, an additional controller gate. useSettleGate(loaded, pageSettleGate); useSettleGate(loaded, gate ?? null); if (Loaded) { const componentProps = props ?? {}; return /*#__PURE__*/_jsx(Loaded, { ...componentProps }); } // While loading, show the explicit `fallback` prop if given, otherwise the // coordinating swap's fallback (e.g. CodeHighlighter's `ContentLoading`). return /*#__PURE__*/_jsx(React.Fragment, { children: fallback ?? coordinated.fallback ?? null }); }