1

Using React and API calls, I separated the API fetch to a different class. The goal is to create an instance of the API class after that to call the "api_call()" method which fetches the data. The data is then stored in the instance object, and I would then take that data and change the React State based on whatever data that is.

However, an important part is awaiting for the fetch response, But I'm not sure where to implement it. Would you implement async await inside the methods in the API class? or would you implement async await from where you call the api_call method in the React class?

React Class

API obj is instantiated and the fetch process is called

class App extends Component {
  constructor(props){
    super(props);

    this.state = {
    }
  }

  componentDidMount(){
    var RESTAPI = new API();
    RESTAPI.api_call();
    console.log(RESTAPI.infomation);
  }
 }

API Class

class API {
  constructor(){
    this.url = "https://restcountries.eu/rest/v2/all";
    this.data = null;
    this.information = null;
  }

  api_call(){    
    fetch(this.url)
    .then((response) => response.json())
    .then(function(data){
      this.data = data;
      this.api_sort();
    }.bind(this))
  }

  api_sort(){    
    var infoObj = {
      name: this.data[0].name
    }

    this.information = infoObj;
  }
}
Saeed
  • 2,169
  • 2
  • 13
  • 29
user10265865
  • 159
  • 2
  • 4
  • 11
  • If you don't return values from your methods, you aren't going to be `await`ing anything. – Jared Smith Sep 06 '18 at 18:42
  • You don't "implement async/await". You can use `async`/`await` syntax to deal with promises. And you have to deal with promises at both places, as asynchronous stays asynchronous. – Bergi Sep 06 '18 at 19:31

3 Answers3

1

api_call is already creating a promise, just have it return that promise

api_call(){    
  return fetch(this.url) // <--- added return statement
   .then((response) => response.json())
   .then(function(data){
     this.data = data;
     this.api_sort();
   }.bind(this))
 }

And then wait for that promise to resolve:

async componentDidMount(){
  const RESTAPI = new API();
  await RESTAPI.api_call();
  console.log(RESTAPI.infomation);
}

Also, you should consider making the promise resolve with the relevant data, instead of resolving to undefined.

api_call(){    
  return fetch(this.url)
   .then((response) => response.json())
   .then((data) => { // <-- changed to arrow function, so there's no need to bind
     this.data = data;
     this.api_sort();
     return this.information; // <--- added return statement
   })
 }

Then you can await it like this:

async componentDidMount(){
  const RESTAPI = new API();
  const information = await RESTAPI.api_call();
  console.log(information)
}
Nicholas Tower
  • 72,740
  • 7
  • 86
  • 98
1

Here's a simple way of achieving this, using constants and functions as properties of those constants, no need to create classes. This sets functions as properties of a utilities handler, which is useful if you want to add more functions to your api.

Call API and sort

API Class

const values = {
    url: "https://restcountries.eu/rest/v2/all"
};

const api = {
    call: async function(url = null){    
        try{
            if(url === null){
                url = values.url;
            }

            let response = await fetch(url, options);
            let responseOK = response && response.ok;
            if (responseOK) {
                let data = await response.json();
                return api.sort(data);
            } else {
                // handle response not ok
            }
        } catch(e){
            // handle promise errors
        }
    },

    sort: function(data){
        // return sorted data
    }
}

export default api;

React Class import api from 'api.js';

class App extends Component {
    constructor(props){
        super(props);

        this.state = {
        }
    }

    async componentDidMount() {
        let result = await api.call(); // or api.call(someUrl)
        // do something with result
    }
}

Sort after API call only if needed

With this solution you could also separate the api call from the sorting of data, like this:

API Class

const values = {
    url: "https://restcountries.eu/rest/v2/all"
};

const api = {
    call: async function(url = null){    
        try{
            if(url === null){
                url = values.url;
            }

            let response = await fetch(url, options);
            let responseOK = response && response.ok;
            if (responseOK) {
                return await response.json();
            } else {
                // handle response not ok
            }
        } catch(e){
            // handle promise errors
        }
    },

    sort: function(data){
        // return sorted data
    }
}

export default api;

React Class import api from 'api.js';

class App extends Component {
    constructor(props){
        super(props);

        this.state = {
            sort: true,
        }
    }

    async componentDidMount() {
        let result = await api.call(); // or api.call(someUrl)
        // do something with result
        // sort if necessary
        let sortedResult = api.sort(result);
        // do something with sortedResult
    }
}

You can alternatively use Axios as well, as seen in this answer.

Remember to handle unresolved promises correctly using try/catch if using it this way.

c-chavez
  • 7,237
  • 5
  • 35
  • 49
  • Don't default-export an object with methdos. Use multiple named exports instead. – Bergi Sep 06 '18 at 19:33
  • Hi @Bergi any reason for doing multiple exports instead of one? performance, best practices or something in particular? It would be very interesting to read more about this, if you have any source on this please share :) – c-chavez Sep 06 '18 at 19:42
  • All of them (performance and best practice), also maintainability. Plain function declarations are just simpler. – Bergi Sep 06 '18 at 19:44
0

At first I wouldn't use a class for the API, it doesnt really have instances so it could be:

 function getData(url) {
   let response;
   return function() {
     if(response) return response;
     return response = fetch(url)
        .then(res => res.json())
        .then(res => res.data);
   };
}

 const API = {
  all: getData("https://restcountries.eu/rest/v2/all"),
  //...
 }

Then inside the component you could await the response:

 async componentDidMount() {
   const { 0: {name} } = await API.all();
   this.setState({ name });
 }

Then you can use name inside the component.

Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151