Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use Async/Await with Axios in React.js

Following

How to use async/await with axios in react

I am trying to make a simple get request to my server using Async/Await in a React.js App. The server loads a simple JSON at /data which looks like this

JSON

{    id: 1,    name: "Aditya" } 

I am able to get the data to my React App using simple jquery ajax get method. However, I want to make use of axios library and Async/Await to follow ES7 standards. My current code looks like this:

class App extends React.Component{  async getData(){      const res = await axios('/data');      console.log(res.json());  }  render(){      return(          <div>              {this.getData()}          </div>      );  } } 

Using this approach I get the following error:

Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead.

Am I not implementing it correctly?

like image 335
Singh Avatar asked Oct 13 '17 15:10

Singh


People also ask

Does Axios support async await?

Axios is a Promised-based JavaScript library that is used to send HTTP requests. You can think of it as an alternative to JavaScript's native fetch() function. We will be using features such as Promises, async/await , and other modern JavaScript design patterns in this tutorial.

Is Axios get async?

Axios is a promise based HTTP client for the browser and Node. js. Axios makes it easy to send asynchronous HTTP requests to REST endpoints and perform CRUD operations. It can be used in plain JavaScript or with a library such as Vue or React.


2 Answers

Two issues jump out:

  1. Your getData never returns anything, so its promise (async functions always return a promise) will be fulfilled with undefined if it doesn't reject

  2. The error message clearly shows you're trying to directly render the promise getData returns, rather than waiting for it to settle and then rendering the fulfillment value

Addressing #1: getData should return the result of calling json:

async getData(){    const res = await axios('/data');    return await res.json(); } 

Addressig #2: We'd have to see more of your code, but fundamentally, you can't do

<SomeElement>{getData()}</SomeElement> 

...because that doesn't wait for the resolution. You'd need instead to use getData to set state:

this.getData().then(data => this.setState({data}))               .catch(err => { /*...handle the error...*/}); 

...and use that state when rendering:

<SomeElement>{this.state.data}</SomeElement> 

Update: Now that you've shown us your code, you'd need to do something like this:

class App extends React.Component{     async getData() {         const res = await axios('/data');         return await res.json(); // (Or whatever)     }     constructor(...args) {         super(...args);         this.state = {data: null};     }     componentDidMount() {         if (!this.state.data) {             this.getData().then(data => this.setState({data}))                           .catch(err => { /*...handle the error...*/});         }     }     render() {         return (             <div>                 {this.state.data ? <em>Loading...</em> : this.state.data}             </div>         );     } } 

Futher update: You've indicated a preference for using await in componentDidMount rather than then and catch. You'd do that by nesting an async IIFE function within it and ensuring that function can't throw. (componentDidMount itself can't be async, nothing will consume that promise.) E.g.:

class App extends React.Component{     async getData() {         const res = await axios('/data');         return await res.json(); // (Or whatever)     }     constructor(...args) {         super(...args);         this.state = {data: null};     }     componentDidMount() {         if (!this.state.data) {             (async () => {                 try {                     this.setState({data: await this.getData()});                 } catch (e) {                     //...handle the error...                 }             })();         }     }     render() {         return (             <div>                 {this.state.data ? <em>Loading...</em> : this.state.data}             </div>         );     } } 
like image 74
T.J. Crowder Avatar answered Oct 06 '22 11:10

T.J. Crowder


In my experience over the past few months, I've realized that the best way to achieve this is:

class App extends React.Component{   constructor(){    super();    this.state = {     serverResponse: ''    }   }   componentDidMount(){      this.getData();   }   async getData(){    const res = await axios.get('url-to-get-the-data');    const { data } = await res;    this.setState({serverResponse: data})  }  render(){   return(      <div>        {this.state.serverResponse}      </div>   );  } } 

If you are trying to make post request on events such as click, then call getData() function on the event and replace the content of it like so:

async getData(username, password){  const res = await axios.post('url-to-post-the-data', {    username,    password  });  ... } 

Furthermore, if you are making any request when the component is about to load then simply replace async getData() with async componentDidMount() and change the render function like so:

render(){  return (   <div>{this.state.serverResponse}</div>  ) } 
like image 26
Singh Avatar answered Oct 06 '22 12:10

Singh