Home > database >  How can I load a spinner while fetching data from API (React)
How can I load a spinner while fetching data from API (React)

Time:08-28

I am using react useParams to show a singleMovie (details). I have also a condition that if there is no picture (in the API) to show a stock picture. However, the problem is that when I access the single movie detail the stock photo shows first then the API picture.

How can I fix this or add a spinner so that I dont see the stock photo first.

import React from 'react'
import { Link, useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
import { API_KEY, IMAGE_BASE_URL } from '../config';
import NoCover from '../img/no-cover.jpg'





const SingleMovie = () => {
  const { movieId } = useParams();
  const MOVIE_API = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${API_KEY}&language=en-US`;
  //useState
  const [movieDetails, setMovieDetails] = useState([])
  //functions
  const getMovieDetails = async (API) => {
    const response = await fetch(API)
    const data = await response.json()
    //console.log(data);
    setMovieDetails(data)
    
  }
  //useEffect
  useEffect(() => {
    getMovieDetails(MOVIE_API)
  }, [MOVIE_API])
  
  const { title, poster_path } = movieDetails
  
  return (
    <>
      <h1>{title}</h1>
      <div>
        {poster_path ? (<img src={`${IMAGE_BASE_URL}${poster_path}`} alt={title} />) : (<img src={NoCover} alt={title} />)}
      </div>
      <Link to="/">Go Back</Link>
      
      </>
    
    );
}

export default SingleMovie

CodePudding user response:

Hey what you can do is create a boolean useState to display a Loading... while getting the data. Like this:

Add:

const [loading, setLoading] = useState(false);

The function that gets the data, there I added a try catch so also you see if there is an error.

const getMovieDetails = async (API) => {
    setLoading(true);
    try {
      const response = await fetch(API);
      const data = await response.json();
      //console.log(data);
      setMovieDetails(data);
    } catch (error) {
      console.log('Error', error);
    } finally {
      setLoading(false);
    }
  };

And then you can add this ternary in the return:

{!loading ? (
        <div>
          {poster_path ? (
            <img src={`${IMAGE_BASE_URL}${poster_path}`} alt={title} />
          ) : (
            <img src={NoCover} alt={title} />
          )}
        </div>
      ) : (
        'Loading...'
      )}

Where I added the text Loading..., you can add a spinner component or whatever you want!

CodePudding user response:

define a state variable loading can be solved

Try out this code

import React from 'react'
import { Link, useParams } from 'react-router-dom';
import { useState, useEffect } from 'react';
import { API_KEY, IMAGE_BASE_URL } from '../config';
import NoCover from '../img/no-cover.jpg'





const SingleMovie = () => {
  const { movieId } = useParams();
  const MOVIE_API = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${API_KEY}&language=en-US`;
  //useState
  const [movieDetails, setMovieDetails] = useState([])
  const [loading, setLoading] = React.useState(true); //    
  //functions
  const getMovieDetails = async (API) => {
    const response = await fetch(API)
    if(response.ok){ //    
        const data = await response.json()
       //console.log(data);
       setMovieDetails(data);
       setLoading(false); //    
    } //    
    
  }
  //useEffect
  useEffect(() => {
    getMovieDetails(MOVIE_API)
  }, [MOVIE_API])
  
  const { title, poster_path } = movieDetails
  
  return (
    <>
      <h1>{title}</h1>
      //    
      {loading ? "spinner" : (<>
      <div>
        {poster_path ? (<img src={`${IMAGE_BASE_URL}${poster_path}`} alt={title} />) : (<img src={NoCover} alt={title} />)}
      </div>
      </>)}
      //    
      <Link to="/">Go Back</Link>
      
      </>
    
    );
}

export default SingleMovie

  • Related