I have a CSR app with the following route definitions:
const router = createBrowserRouter([
{
path: '/app/',
element: <NavBar />,
loader: navBarLoader,
children: [
{
path: 'placement-selection',
Component: PlacementSelection,
loader: placementSelectionLoader,
children: [
{
path: 'options',
Component: PlacementSelectionOptions,
loader: placementSelectionOptionsLoader,
children: [
{
path: 'summary',
Component: PlacementSelectionOptionsSummary,
loader: placementSelectionOptionsSummaryLoader,
},
],
},
],
},
],
},
]);
And the following loaders:
export async function navBarLoader() {
console.log('called navBarLoader')
return await new Promise((res, rej) => {
setTimeout(() => res('response'), 2000)
})
}
export async function placementSelectionLoader() {
console.log('called placementSelectionLoader')
return await new Promise((res, rej) => {
setTimeout(() => res('response'), 2000)
})
}
export async function placementSelectionOptionsLoader() {
console.log('called placementSelectionOptionsLoader')
return await new Promise((res, rej) => {
setTimeout(() => res('response'), 2000)
})
}
export async function placementSelectionOptionsSummaryLoader() {
console.log('called placementSelectionOptionsSummaryLoader')
return new Promise((res, rej) => {
setTimeout(() => res('response'), 10000)
})
}
This is how PlacementSelectionOptionsSummary
is using the loader data:
import { Suspense } from 'react';
import './styles.css';
import { Await, useLoaderData } from 'react-router';
export default function PlacementSelectionOptionsSummary() {
const loaderData = useLoaderData();
return (
<>
<Suspense fallback={<div>Loading...</div>}>
<Await resolve={loaderData}>
{value => <h3>Non critical value: {value}</h3>}
</Await>
</Suspense>
<div className="main-container" style={{ width: '30%' }}>
<div className="container" style={{ height: '500px' }}>
<div className="card" style={{ backgroundColor: 'blue' }}>
<span className="description">
{' '}
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean
tempor ante ligula, id aliquet ligula laoreet id. In aliquam
hendrerit mauris, at sagittis tortor efficitur non. Proin egestas
facilisis augue. Sed aliquet maximus tempus. Nullam ornare
efficitur blandit. Sed eu mattis tortor. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Aenean tempor ante ligula, id
aliquet ligula laoreet id. In aliquam hendrerit mauris, at
sagittis tortor efficitur non. Proin egestas facilisis augue. Sed
aliquet maximus tempus. Nullam ornare efficitur blandit. Sed eu
mattis tortor',
</span>
</div>
</div>
</div>
</>
);
}
As you can see, placementSelectionOptionsSummaryLoader
takes 10 seconds, where all others take 2 seconds.
Based on what's mentioned here: , I figured that all I need to do was to just return the Promise directly, which I am doing for this loader.
Still, the UI for the route /app/placement-selection/options/summary
waits for all of these loaders to "finish" before the UI renders.
How can I get most of the UI to render, and then only have PlacementSelectionOptionsSummary
render a fallback until it's slow-10-seconds-loader finishes?
I have a CSR app with the following route definitions:
const router = createBrowserRouter([
{
path: '/app/',
element: <NavBar />,
loader: navBarLoader,
children: [
{
path: 'placement-selection',
Component: PlacementSelection,
loader: placementSelectionLoader,
children: [
{
path: 'options',
Component: PlacementSelectionOptions,
loader: placementSelectionOptionsLoader,
children: [
{
path: 'summary',
Component: PlacementSelectionOptionsSummary,
loader: placementSelectionOptionsSummaryLoader,
},
],
},
],
},
],
},
]);
And the following loaders:
export async function navBarLoader() {
console.log('called navBarLoader')
return await new Promise((res, rej) => {
setTimeout(() => res('response'), 2000)
})
}
export async function placementSelectionLoader() {
console.log('called placementSelectionLoader')
return await new Promise((res, rej) => {
setTimeout(() => res('response'), 2000)
})
}
export async function placementSelectionOptionsLoader() {
console.log('called placementSelectionOptionsLoader')
return await new Promise((res, rej) => {
setTimeout(() => res('response'), 2000)
})
}
export async function placementSelectionOptionsSummaryLoader() {
console.log('called placementSelectionOptionsSummaryLoader')
return new Promise((res, rej) => {
setTimeout(() => res('response'), 10000)
})
}
This is how PlacementSelectionOptionsSummary
is using the loader data:
import { Suspense } from 'react';
import './styles.css';
import { Await, useLoaderData } from 'react-router';
export default function PlacementSelectionOptionsSummary() {
const loaderData = useLoaderData();
return (
<>
<Suspense fallback={<div>Loading...</div>}>
<Await resolve={loaderData}>
{value => <h3>Non critical value: {value}</h3>}
</Await>
</Suspense>
<div className="main-container" style={{ width: '30%' }}>
<div className="container" style={{ height: '500px' }}>
<div className="card" style={{ backgroundColor: 'blue' }}>
<span className="description">
{' '}
'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean
tempor ante ligula, id aliquet ligula laoreet id. In aliquam
hendrerit mauris, at sagittis tortor efficitur non. Proin egestas
facilisis augue. Sed aliquet maximus tempus. Nullam ornare
efficitur blandit. Sed eu mattis tortor. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Aenean tempor ante ligula, id
aliquet ligula laoreet id. In aliquam hendrerit mauris, at
sagittis tortor efficitur non. Proin egestas facilisis augue. Sed
aliquet maximus tempus. Nullam ornare efficitur blandit. Sed eu
mattis tortor',
</span>
</div>
</div>
</div>
</>
);
}
As you can see, placementSelectionOptionsSummaryLoader
takes 10 seconds, where all others take 2 seconds.
Based on what's mentioned here: https://reactrouter/how-to/suspense, I figured that all I need to do was to just return the Promise directly, which I am doing for this loader.
Still, the UI for the route /app/placement-selection/options/summary
waits for all of these loaders to "finish" before the UI renders.
How can I get most of the UI to render, and then only have PlacementSelectionOptionsSummary
render a fallback until it's slow-10-seconds-loader finishes?
1 Answer
Reset to default 1You should return an object from the route loaders where the Promise you are awaiting, or deferring actually, is a property on that returned object.
Example:
function placementSelectionOptionsSummaryLoader() {
console.log("called placementSelectionOptionsSummaryLoader");
const data = new Promise((res, rej) => {
setTimeout(() => res("response"), 10000);
});
return { data };
}
Then update PlacementSelectionOptionsSummary
to use the useLoaderData
hook and wait for the returned loaderData.data
property to resolve.
const PlacementSelectionOptionsSummary = () => {
const loaderData = useLoaderData();
return (
<>
<Suspense fallback={<div>Loading...</div>}>
<Await resolve={loaderData.data}> // <-- wait for data property
{(value) => <h3>Non critical value: {value}</h3>}
</Await>
</Suspense>
<div className="main-container" style={{ width: "30%" }}>
<div className="container" style={{ height: "500px" }}>
<div className="card" style={{ backgroundColor: "blue" }}>
<span className="description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean
tempor ante ligula, id aliquet ligula laoreet id. In aliquam
hendrerit mauris, at sagittis tortor efficitur non. Proin egestas
facilisis augue. Sed aliquet maximus tempus. Nullam ornare
efficitur blandit. Sed eu mattis tortor. Lorem ipsum dolor sit
amet, consectetur adipiscing elit. Aenean tempor ante ligula, id
aliquet ligula laoreet id. In aliquam hendrerit mauris, at
sagittis tortor efficitur non. Proin egestas facilisis augue. Sed
aliquet maximus tempus. Nullam ornare efficitur blandit. Sed eu
mattis tortor
</span>
</div>
</div>
</div>
</>
);
};
PlacementSelectionOptionsSummary
route loader takes the longest to complete. What isPlacementSelectionOptionsSummary
doing with the returned Promise? Can you edit to include a complete minimal reproducible example so readers can better help you with thePlacementSelectionOptionsSummary
component UI? – Drew Reese Commented Apr 1 at 20:50