/*** Copyright (c) Meta Platforms, Inc. and affiliates.** This source code is licensed under the MIT license found in the* LICENSE file in the root directory of this source tree.** @flow*/import * as React from 'react';
import {
Fragment,
Suspense,
unstable_SuspenseList as SuspenseList,
useState,
} from 'react';
function SuspenseTree(): React.Node {
return (
<Fragment>
<h1>Suspense</h1>
<h4>Primary to Fallback Cycle</h4>
<PrimaryFallbackTest initialSuspend={false} />
<h4>Fallback to Primary Cycle</h4>
<PrimaryFallbackTest initialSuspend={true} />
<NestedSuspenseTest />
<SuspenseListTest />
<EmptySuspense />
</Fragment>
);}function EmptySuspense() {return <Suspense />;
}// $FlowFixMe[missing-local-annot]function PrimaryFallbackTest({initialSuspend}) {
const [suspend, setSuspend] = useState(initialSuspend);
const fallbackStep = useTestSequence('fallback', Fallback1, Fallback2);
const primaryStep = useTestSequence('primary', Primary1, Primary2);
return (
<Fragment>
<label>
<input
checked={suspend}
onChange={e => setSuspend(e.target.checked)}
type="checkbox"
/>Suspend
</label><br /><Suspense fallback={fallbackStep}>
{suspend ? <Never /> : primaryStep}
</Suspense>
</Fragment>
);
}function useTestSequence(label: string, T1: any => any, T2: any => any) {
const [step, setStep] = useState(0);
const next: $FlowFixMe = (
<button onClick={() => setStep(s => (s + 1) % allSteps.length)}>
next {label} content
</button>
);const allSteps: $FlowFixMe = [
<Fragment>{next}</Fragment>,<Fragment>{next} <T1 prop={step}>mount</T1></Fragment>,<Fragment>{next} <T1 prop={step}>update</T1></Fragment>,<Fragment>{next} <T2 prop={step}>several</T2> <T1 prop={step}>different</T1>{' '}<T2 prop={step}>children</T2></Fragment>,<Fragment>{next} <T2 prop={step}>goodbye</T2></Fragment>,];
return allSteps[step];
}function NestedSuspenseTest() {return (<Fragment><h3>Nested Suspense</h3><Suspense fallback={<Fallback1>Loading outer</Fallback1>}><Parent /></Suspense></Fragment>);}function Parent() {return (<div><Suspense fallback={<Fallback1>Loading inner 1</Fallback1>}><Primary1>Hello</Primary1></Suspense>{' '}<Suspense fallback={<Fallback2>Loading inner 2</Fallback2>}><Primary2>World</Primary2></Suspense><br /><Suspense fallback={<Fallback1>This will never load</Fallback1>}><Never /></Suspense><br /><b><LoadLater /></b></div>);}function SuspenseListTest() {return (<><h1>SuspenseList</h1><SuspenseList revealOrder="forwards" tail="collapsed"><div><Suspense fallback={<Fallback1>Loading 1</Fallback1>}><Primary1>Hello</Primary1></Suspense></div><div><LoadLater /></div><div><Suspense fallback={<Fallback2>Loading 2</Fallback2>}><Primary2>World</Primary2></Suspense></div></SuspenseList></>);}function LoadLater() {const [loadChild, setLoadChild] = useState(false);
return (<Suspensefallback={<Fallback1 onClick={() => setLoadChild(true)}>Click to load</Fallback1>}>{loadChild ? (
<Primary1 onClick={() => setLoadChild(false)}>Loaded! Click to suspend again.
</Primary1>) : (<Never />)}</Suspense>);}function Never() {throw new Promise(resolve => {});}function Fallback1({prop, ...rest}: any) {
return <span {...rest} />;
}function Fallback2({prop, ...rest}: any) {
return <span {...rest} />;
}function Primary1({prop, ...rest}: any) {
return <span {...rest} />;
}function Primary2({prop, ...rest}: any) {
return <span {...rest} />;
}export default SuspenseTree;