4

I am trying to fetch cryptocurrency data from an api, by updating my state with the information pulled up. But when I do, my console.log is showing an empty object. And there is no data being passed down to my child components either. Did I do something wrong here?

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

function App() {
  const [smallData, setSmallData] = useState({})

  async function getAPI(){
    await fetch(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false
    `)
    .then(response => response.json())
    .then(data => setSmallData({data})); 
  }

  useEffect( () => {
    getAPI();
    console.log(smallData)
  }, []);


  return (
    <div className="app">
      
      {/* SmallBox - balance */}
      <SmallBox className="balance"/>
      
      {/* SmallBox - bitcoin */}
      <SmallBox className="bitcoin" coin={smallData}/>
      
      {/* SmallBox - ethereum */}
      <SmallBox className="ethereum" coin={smallData}/>
      
      {/* SmallBox - ripple */}
      <SmallBox className="ripple" coin={smallData}/>
      
      {/* SmallBox - tether */}
      <SmallBox className="tether" coin={smallData}/>
     
    </div>
  );
}

export default App;
2
  • getAPI is an async function. When you call console.log the state is not updated yet. Commented Nov 28, 2020 at 21:31
  • Both your API call and react state changes are asynchronous, you can' print the state value on the next line and see it updated Commented Nov 28, 2020 at 21:32

5 Answers 5

2

Try awaiting for the data to be fetched before logging it, instead of await for the fetch inside the async function getAPI:

  function getAPI(){
    return fetch(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false
    `)
      .then(response => response.json())
      .then(data => setSmallData({data})); 
  }

  useEffect( () => {
    getAPI()
      .then(() => console.log(smallData));
  }, []); // <- Add dependencies here

Note: I think getAPI is going to be called on every render of the component, which I don't think it's what you want. I suggest to add some dependency to the second variable (an empty array) passed to the useEffect function, telling React to call this effect only when that/those variables have changed. For example, you can add the currency.

Sign up to request clarification or add additional context in comments.

2 Comments

This would return a warning about creating race conditions since useEffect is synchronous function. To prevent it, as other suggests it is better to create async function inside useEffect and await inside that
@AmirNabaei, you're right. Changed await with then ;)
1

change useEffect wait for the getApi function to finish.

  useEffect(() => {
    getAPI().then((_) => {
      console.log(smallData);
    });
  }, []);

Comments

1

Try this:

async function getAPI(){
    const rawData = await fetch(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false`)
    const jsonData = rawData.json();
    setSmallData( state => ({...jsonData}) );
  }

useEffect(() => {
    getAPI().then(() => console.log(smallData));
  }, []);

Comments

0

First, your console.log should display {} because that's the initial state of smallData, and getAPI will be executed asynchronously. There's nothing wrong about that.

But with your problem of no data being passed down to children components, it might be because you are setting the smallData to { data: *api response* }, maybe you want smallData to be the api response, instead of an object with a data property.

I would suggest to put console.log(smallData) just before the return statement, just to see its content in every render.

If you still are facing problems, add the SmallBox component code, maybe the problem is there

Comments

0

Your React set State hooks are called asynchronously. Thus, your smallData will show the previous state because the component has not rerendered yet.

You didn't do anything wrong. See below for commented out code to see what's going on.

  useEffect( () => {
    getAPI(); // 1. This call seems to be correct, and queues up setSmallData. 
    // 2. The state update has not happened yet, but the setSmallData call is queued up. 
    console.log(smallData) // 3. still {} because the state has not been updated yet, and thus is still showing the current state. 

  }, []);

In other words, the console.log(smallData) runs BEFORE your setSmallData, and that's why you see an empty object(because {} is your initial state). If you displayed the data somewhere on the page however, you will notice that the component rerenders with the data.

You can try:

    async function getAPI(){
      return await fetch(`...`)
      .then(response => response.json())
      .then(data => data); 
    }
    useEffect( () => {
     async function getApiAndSetState() {
     let data = await getAPI(); // the code pauses here until getAPI() completes.
     console.log(data); // this data is now available
     setSmallData(data);
     }
   }, []);

Comments

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.