Loader loads for a fraction of a second only, in case there is a delay in API response, I have attached a screen recording for your reference.
Is there any way I can display the loader from the moment the API is called inside of React.Suspense
itself? Attached is a screen recording
Following is my source code.
main.jsx
import React, { useRef } from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, defer, RouterProvider } from "react-router-dom";
import Homepage from "./App";
import "./index.css";
const router = createBrowserRouter([
{
path: "/",
element: <Homepage />,
loader: async ({ request, params }) => {
const data = await fetch('https://api.rapidmock.com/mocks/j4CMM', {
headers : {
"x-rapidmock-delay": 2500
}
});
return defer({
results: data.json(),
});
},
},
]);
ReactDOM.createRoot(document.getElementById("root")).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);
App.jsx
import React, { useState } from "react";
import { useLoaderData, Await, useAsyncValue } from "react-router-dom";
import reactLogo from "./assets/react.svg";
import "./App.css";
const Homepage = () => {
const { results } = useLoaderData();
// Render the data when it is available
return (
<React.Suspense fallback={<p>Loading data...</p>}>
<Await
resolve={results}
errorElement={<p>Error loading data</p>}
>
{(results) => {
console.log("results", results);
return <>Hello</>;
}}
</Await>
</React.Suspense>
);
};
export default Homepage;
It turns out that if you want to actually defer the loader data then you shouldn't be awaiting anything in the loader function, just return the promise.
See deferred.
The "ah-ha" moment came when I read this section in the docs:
You can literally switch between whether something is going to be deferred or not based on whether you include the await keyword:
return defer({ // not deferred: packageLocation: await packageLocationPromise, // deferred: packageLocation: packageLocationPromise, });
When the loader
is async
and awaits
anything at all this actually blocks the loader completing and blocks the route's element mounting and rendering. What you are seeing currently is the UI blocked while the fetch
occurs, and then the route loads and only the response.json
Promise was deferred/awaited.
Return the entire Promise chain.
Example:
loader: ({ request, params }) => {
const results = fetch("https://api.rapidmock.com/mocks/j4CMM", {
headers: {
"x-rapidmock-delay": 2500
}
}).then((response) => response.json());
return defer({ results });
}