0

I have an app that fetches data from an api. Whilst fetching there is a loading state that displays an icon to the screen. However this is a quick flash. I would like to display the loading icon on screen for a 2 seconds to improve UI and let the user know something is happening.

Here is my code:

 const [info, setInfo] = useState({});
 const [loading, setLoading] = useState(false);

useEffect(() => {
    setLoading(true);
    axios
      .get(`https://restcountries.eu/rest/v2/alpha/${code}`)
      .then((response) => {
        console.log(response);
        setInfo(response.data);
        setLoading(false);
      });

  }, []);

 return (
    <div>
      <Link to='/'>
        <button>Back</button>
      </Link>
      {loading ? (
        <LoadingIcon />
      ) : (
        <CountryInfo
          info={info}
          borders={borders}
          languages={languages}
          currencies={currencies}
        />
      )}
    </div>
  );
};

5 Answers 5

3

you can use promise.all

So even if your request comes early your loading will be shown for at least 2 seconds.

setLoading(true);
const fetchPromise = axios
  .get(`https://restcountries.eu/rest/v2/alpha/${code}`);
const timeOutPromise = new Promise(resolve => {
  setTimeout(resolve, 2000);
})

Promise.all([fetchPromise, timeOutPromise]).then(([response]) => {
  console.log(response);
  setInfo(response.data);
  setLoading(false);
})
Sign up to request clarification or add additional context in comments.

2 Comments

I tried this and it gives the following error. Unhandled Rejection (TypeError): object is not iterable (cannot read property Symbol(Symbol.iterator))
@DGB my bad! it should be [fetchPromise, timeOutPromise].
0

api call is async as soon as the data loads the loading us set to false so you only see it for a second.

Alternatively you can also do this.

{Object.entries(info).length === 0 && info.constructor === Object ? (
        <LoadingIcon />
      ) : (
        <CountryInfo
          info={info}
          borders={borders}
          languages={languages}
          currencies={currencies}
        />

Comments

0
  useEffect(() => {
    setLoading(true);

    const request = axios
      .get(`https://restcountries.eu/rest/v2/alpha/${code}`);

    const timer = new Promise(resolve => setTimeout(resolve, 2000));

    return Promise.all([request, timer]).then(([response]) => {
      console.log(response);
      setInfo(response.data);
      setLoading(false);
    });    
  }, []);

Comments

0

Just add setTimeout(()=>setLoading(false),2000) in the axios callback. Note this will add an additional 2 seconds to the load time, so be sure to adjust accordingly.

2 Comments

problem with this code is even if server takes more time loader will be shown for extra 2 seconds.
He mentioned it was just flashing so I doubt the latency will fluctuate that much. No need to overcomplicate.
0

add setTimeout in success callback.

useEffect(() => {
    setLoading(true);
    axios
      .get(`https://restcountries.eu/rest/v2/alpha/${code}`)
      .then((response) => {
        console.log(response);
        setTimeout(function(){
            setInfo(response.data);
            setLoading(false);
        },2000)
      });

  }, []);

2 Comments

This worked, however could you explain something to me? Does this wait 2000 milliseconds before setInfo? I'm asking because I wonder if there would be any issues with this. Thanks.
@DGB yes, it will wait till 2000 ms

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.