How do I conditionally render a spinner while waiting for a new image to load?

I am writing a simple react app that displays a “photo of the day” from the NASA API. I have added a date input that allows the user to choose different previous dates and show those dates’ photo. Since the image is sometimes really large, I’d like to show a spinner till the image completely loads before showing it.

I have tried adding a isLoaded state to the Photo component and then conditionally rendering a spinner while waiting for the image to load. My idea was that when img onLoad fires I change isLoaded to true and render the image. It doesn’t really seem to work.

// App.js
function App() {
  const [pod, setPod] = useState({});
  const [date, setDate] = useState(today);

  const handleDateChange = e => {
    setDate(e.target.value);
  };

  useEffect(() => {
    axios
      .get(`${nasa_api}&date=${date}`)
      .then(res => {
        console.log(res.data);
        setPod(res.data);
      })
      .catch(err => {
        console.error(err);
      });
  }, [date]);

  return (
    <div className="App">
      {Object.entries(pod).length ? (
        <>
          <Photo title={pod.title} date={date} url={pod.url} />
          <DatePicker date={date} handleDateChange={handleDateChange} />
          <Explanation explanation={pod.explanation} />
          <Footer copyright={pod.copyright} />
        </>
      ) : (
        <ReactLoading
          className="spinner"
          type="spin"
          color="blue"
          height="5%"
          width="5%"
        />
      )}
    </div>
  );
}
// Photo.js
function Photo(props) {
  return (
    <div className="photo">
      <h1>{props.title}</h1>
      <h3>Date: {props.date}</h3>
      <img src={props.url} alt="NASA Photo of the Day" />
    </div>
  );
}

I would like every time the date is changed, a spinner to show until the image is loaded and then the image can be shown. How can I do this?

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

Add an isLoading state and set/unset it in your useEffect:

  const [isLoading, setIsLoading] = useState(false)

  // ... //

  useEffect(() => {
    setIsLoading(true)
    axios
      .get(`${nasa_api}&date=${date}`)
      .then(res => {
        console.log(res.data);
        setPod(res.data);
      })
      .catch(err => {
        console.error(err);
      }).then(()=>{
         setIsLoading(false)
      });
  }, [date]);

Then use it in in your return:

  return (
    <div className="App">
      {!isLoading && Object.entries(pod).length ? (
        <>
          <Photo title={pod.title} date={date} url={pod.url} />
          <DatePicker date={date} handleDateChange={handleDateChange} />
          <Explanation explanation={pod.explanation} />
          <Footer copyright={pod.copyright} />
        </>
      ) : (
        <ReactLoading
          className="spinner"
          type="spin"
          color="blue"
          height="5%"
          width="5%"
        />
      )}
    </div>
  );

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply