javascriptreactjsreact-routerreact-router-dom

react-router 6.0 - useLoaderData returns a promise. Cannot display data


https://github.com/erAnusriM/react_router_trial The above is my github repository. How do I achieve to print the list of users returned from the API in App.jsx file When I print the value of result on console, it gives me a Response object.

Expected Behavior The result should be an array object

Actual Behavior The result is a Response object

Reference https://reactrouter.com/en/main/hooks/use-loader-data
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 = fetch('https://jsonplaceholder.typicode.com/users');

      return defer({
        results: data,
      });
    },
  },
]);

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); <== **prints results as a Response object not array**
       return <>Hello</>;
     }}
  
    
  </Await>
</React.Suspense>
};

export default Homepage;

Solution

  • In your loader function data is the fetch response value, not the actual data. "https://jsonplaceholder.typicode.com/users" returns a JSON value that must also be accessed. This is another Promise that should be awaited.

    A typical loader flow may look something like:

    loader: async ({ request, params }) => {
      const response = await fetch("https://jsonplaceholder.typicode.com/users");
    
      return { results: response.json() };
    }
    
    const { results } = useLoaderData();
    

    The defer utility (v6 only1) is used when this loader may take more time than is usual.

    loader: async ({ request, params }) => {
      const response = await fetch("https://jsonplaceholder.typicode.com/users");
    
      return defer({
        results: response.json()
      });
    }
    
    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 }); // <-- array of objects
              return <>Hello</>;
            }}
          </Await>
        </React.Suspense>
      );
    };
    

    1 Both json and defer are deprecated in v7, now just return raw objects directly, they are deferred by default, or you can await all Promises to settle in the loaders/actions to not defer them. See V6 Deprecations for details.