Add Async/Await to React Fetch Request

I’m making a fetch request to this API and I’m successfully getting the data and printing it to the console. However I’m new to asynchronous calls in Javascript/React. How do add async/await in this code to delay the render upon successful fetch? I’m getting the classic Uncaught (in promise) TypeError: Cannot read property '0' of undefined because I believe that the DOM is trying to render the data that hasn’t been fully fetched yet.

import React, { useEffect, useState } from "react";

export default function News() {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [stories, setStory] = useState([]);

  useEffect(() => {
    fetch(
      "http://api.mediastack.com/v1/news"
    )
      .then((res) => res.json())
      .then(
        (result) => {
          setIsLoaded(true);
          setStory(result);
          console.log(result.data[0]);       // printing to console to test response
          console.log(result.data[0].title); // printing to console to test response
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      );
  }, []);

  if (error) {
    return <div>Error: {error.message}</div>;
  } else if (!isLoaded) {
    return <div>Loading...</div>;
  } else {
    return (
      <div>
        <p>{stories.data[0].title} </p> // this is the where render error is
      </div>
    );
  }
}

27 thoughts on “Add Async/Await to React Fetch Request”

  1. async/await is just another form to retrieve asynchronous data, like you are doing with then.

    The message:

     Cannot read property '0' of undefined
    

    means that ‘result.data’ is undefined.

    Anyway, if it entered the "then" callback it always means that the request was fetched, there is no such thing as "half fetched" or "fully fetched".

    I suggest you to use the debugger, by placing

    debugger;
    

    right in the beginning of the ‘then’ callback to ensure what your result is.
    or you may also console.log the result.

    Just to clarify:

    myFunctionThatReturnsPromise.then(response => //do something with response)
    

    is the same as

    await response = myFunctionThatReturnsPromise;
    
    Reply
  2. You already have async code using Promise.then so async/await will not help you here – it’s just a different way of writing Promise-based functionality.

    Your problem is just as you say – React is trying to render your else block before stories has anything for you to render. I think you just need to write stricter conditions, e.g.

      if (error) {
        return <div>Error: {error.message}</div>;
      }
    
      if (!isLoaded || !stories.data.length) {
        return <div>Loading...</div>;
      }
      
      return (
        <div>
          <p>{stories.data[0].title} </p> // this is the where render error is
        </div>
      );
    
    Reply

Leave a Comment