I am experiencing a problem with useLoaderData() hook, While using react-router Form component with GET method. I have programmed the loader to return deferred data, and I am using Suspense in my component to render a Loading icon until the promise is resolved. This works as intended on the initial render, i.e:
- Loader runs.
- Component renders and runs the Suspense fallback.
- Data is Fetched from the API
- The promise resolves and the callback function in Await runs.
However, when i submit the form again, the following happens:
- Loader runs
- Component renders, but this time the useLoaderData() returns the previously fetched result instead of the promise
- The callback function in Await runs(as there is no promise to resolve).
- The data is fetched from the API.(The URL also doesn’t switch until this point)
- Component re-renders, and useLoaderData() returns the new fetched data.
- The callback function in Await re-runs.
Below is my code:
App component(Router):
function App() {
const browserRouter = createBrowserRouter(
createRoutesFromElements(
<Route
path="/"
element={<HomePage />}
loader={HomePageLoader}
/>
)
);
return (
<>
<Navbar />
<RouterProvider router={browserRouter} />
</>
);
}
Function interacting with API()
export async function fetchDataFromColorAPI(targetUrl: string) {
const response = await fetch(targetUrl);
if (response.status !== 200) {
throw new Error("Error occurred with API");
}
const data = await response.json();
console.log("API data: ",data.colors);
return getColorsInfo(data.colors);
}
Loader Function:
export function loader({ request }: LoaderFunctionArgs) {
console.log("in loader");
const params = new URL(request.url).searchParams;
const { color, mode, count } = getSearchParams(params); //utility function to get search parameters
const baseUrl = "https://www.thecolorapi.com/scheme";
const searchQuery = `?hex=${color}&mode=${mode}&count=${count}&format=json`;
return defer({ colorsInfo: fetchDataFromColorAPI(baseUrl + searchQuery) });
}
Component calling useLoaderData():
export default function HomePage() {
console.log("In component");
const data = useLoaderData() as LoaderData;
function renderPallet(colorsInfo: Color[]) {
console.log("promise resolved, Values recieved: ", colorsInfo);
return <Pallet colorsInfo={colorsInfo} />;
}
return (
<main>
<InputForm />
<Suspense fallback={<h1>Loading....</h1>}>
<Await resolve={data.colorsInfo}>{renderPallet}</Await>
</Suspense>
</main>
);
}
Form:
<Form replace className="form">
//Input fields
</Form>
I tried going through the docs, but couldn’t find the reason for this. My guess is that this may be happening because of some optimization being done by React or React-Router.
Can someone explain this behaviour specifically how loader and useLoaderData() behave when combined with react-router Form “GET” method. How can I force the useLoaderData() hook to return a promise instead of the previous fetched result.
Github Respository: https://github.com/Azfar731/ColorPicker.git
azfar razzaq is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.