Javascript, then method not waiting for async function in React

Viewed 201

I'm trying to set the background of my React App to the NASA APOD (Astronomy Picture of the Day). Here is my code for that:

import './App.css';
import React, { useState, useEffect } from "react";

const apiKey = process.env.REACT_APP_NASA_KEY;

function App() {
  const [bg, setBg] = useState(null);

  async function fetchPhoto() {
    const dat = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`);
    const data = await dat.json();
    setBg(data); 
  }

  useEffect(() => {
    fetchPhoto().then(() => {
      document.body.style.backgroundImage = `url('${bg.url}')`
    })
  }, [])

  return null;
}

However, in this code then does not wait for the state to be set and I get the error:

App.js:22 Uncaught (in promise) TypeError: Cannot read properties of null (reading 'url')at App.js:22:1

What am I doing wrong? Thanks!

2 Answers

1. Solution

A simpler way to achieve what you want is by doing like below, this way you don't have to menage asynchronous tasks like you are doing:

import './App.css';
import React, { useState, useEffect } from "react";

const apiKey = process.env.REACT_APP_NASA_KEY;

function App() {
  const [bg, setBg] = useState(null);

  async function fetchPhoto() {
    const dat = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`);
    const data = await dat.json();
    setBg(data); 
  }
  
  useEffect(() => {
    fetchPhoto()
  }, [])

  useEffect(() => {
    if(bg){
      document.body.style.backgroundImage = `url('${bg.url}')`;
    }
  }, [bg])

  return null;
}

2. Explanation

Your code is not working because just after fetchPhoto() is executed and resolved, React did not yet re-render the components (update the state), therefore bg is still null.

Do you need the state for something else? If not, you could avoid it:

function App() {
    
  useEffect(() => {
    const fetchPhoto = async () => {
      const dat = await fetch(`https://api.nasa.gov/planetary/apod?api_key=${apiKey}`);
      const data = await dat.json();
    
      if (data) {
        document.body.style.backgroundImage = `url('${data.url}')`;
      }
    }

    fetchPhoto();
  }, [])

  return null;
}
Related