Skip to main content

Suspense

Intro:

What Suspense adds is the ability to have a component notify React at render time that it’s waiting for asynchronous data; this is called suspending, and it can happen anywhere in a component’s tree, as many times as needed, until the tree is ready. When a component suspends, React will decline to render the pending state update until all suspended dependencies have been satisfied.

There are many good articles on Suspense, and understanding how it works is important, since it is central to how Threekit Headless works. But simply, whenever a component needs access to something that requires an asynchronous load, that component will initiate the fetch, and then throw a Promise. (Yes, as in throw new Promise()). React will intercept that error, suspend the component, and later retry the render. On the next retry, if the data has still not arrived, the component will throw again. When the data has arrived, the component will continue rendering, with the data appearing as if it was synchronous.

const assetState = useLoader(AssetLoader, uuid);
// We only get to this point when the loader has finished resolving.

Presently, the user is responsible for inserting the Suspense component (and the desired fallback) into the hieararchy. With a single Suspense, if we swappped a model asset out which triggered a new asset load, we would fall back to the blank loading state.

It would be great to be able to provide a fallback for parts of the tree, for example, when loading an asset into an already rendered canvas, we might want to show a placeholder for just that asset.

<Suspense fallback={<Spinner />}>
<group position={[0,1,2]}>
<Suspense fallback={<FurnitureLoading />}>
<Asset id={sofaId} />
</Suspense>
</group>
<group position={[0,2,3]}>
<Suspense fallback={<FurnitureLoading />}>
<Asset id={coffeeTableId} />
</Suspense>
</group>
<Suspense>

Question: How does the user provide a fallback for when the configurator swaps a model?