71

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?

Singh
  • 1,887
  • 3
  • 18
  • 29
  • render() is working just fine as I clearly mentioned that I am able to get details when I use $.ajax(). What extra code should I add? This is a simple get request to the server using ES7 standards. – Singh Oct 13 '17 at 15:38
  • Ideally, update your question with a **runnable** [mcve] demonstrating the problem using a placeholder for the ajax (e.g., `setTimeout` or similar), using Stack Snippets (the `[<>]` toolbar button). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). – T.J. Crowder Oct 13 '17 at 15:43
  • ...but the added code absolutely makes things clear. :-) – T.J. Crowder Oct 13 '17 at 15:48
  • 8
    FYI, `async/await` is part of ES2017, **not** ES7 (ES2016). – Felix Kling Oct 13 '17 at 17:20
  • Thank you for the information. – Singh Oct 13 '17 at 17:24

3 Answers3

92

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>
        );
    }
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • It gave me this error "'this' is not allowed before super()". So I added super(); right before "this.state = {data: null};" which later resulted in a new error: "'getData' is not defined no-undef" – Singh Oct 13 '17 at 15:54
  • @Morfsys: I don't think that's the exact error message. :-) I did say "*something like* this". I've updated the above, was missing `this.` on `getData`. – T.J. Crowder Oct 13 '17 at 15:57
  • 1
    .catch(err => { /*...handle the error...*/}); executes now.It says res.json() is not a function. – Singh Oct 13 '17 at 16:16
  • @Morfsys: Well, then `res` doesn't have a `json` property that's a function. I just copied that from your code, I have no idea what `res` is or whether you should expect it to have a `json` function. (The result from `fetch` would, but I don't use axios.) That has nothing to do with the fundamentals described above, which are the primary thing you need to fix (though of course, you need to fix the `json` thing as well). – T.J. Crowder Oct 14 '17 at 09:00
  • 1
    Just FYI... `return res.json()` would need to be `return await res.json()` in the example above, correct? If you are returning it on the next line, the return line will execute right away instead of waiting to be defined by `const res` right above it. – dave4jr May 09 '18 at 22:13
  • 2
    @dave4jr: No, you don't have to -- but it may be a good idea from a code maintenance standpoint, thanks. *"If you are returning it on the next line, the return line will execute right away instead of waiting..."* No, that's incorrect. The `return` line won't execute until the `await axios('/data')` resolves. So the code without the `await` would work, the promise created by `getData` being `async` would just slave to the one returned by `res.json()`. But from a code maint. pers., you're right, better to `await` it -- because it would be easy to mess it up when making changes to `getData`. – T.J. Crowder May 10 '18 at 05:59
  • @T.J.Crowder Ahh yes you are correct, thanks for the explanation sir. – dave4jr May 14 '18 at 01:03
  • NOTE: In case of error --- e.response.data --- will have the actual error. – Manohar Reddy Poreddy Oct 12 '19 at 13:50
20

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>
 )
}
Singh
  • 1,887
  • 3
  • 18
  • 29
  • 2
    This is basically just my answer, reworded. Also: 1. Don't make `componentWillMount` an `async` function. React will ignore the returned promise. 2. Unless `res.data` is an accessor returning a promise, it doesn't make any sense to use `await` when accessing it. – T.J. Crowder Dec 18 '17 at 08:13
  • 3
    I've merely tried to simplify the answer. No offense, but I think `then` and `catch` is not the latest standard (ES6) of following up with a promise. In addition, res.json() wasn't working for me and so I had to replace it with res.data that comes along with a promise in`GET` or `POST` request. – Singh Dec 18 '17 at 08:34
  • 4
    `then` and `catch` **are** the ES2015 (aka "ES6") way to deal with promises. `async`/`await` are the ES2017 ("ES8") way. But you can only use `await` within an `async` function, and making `componentWillMount` `async` is creating a promise that will never be consumed. If you want to use `await` instead, that's fine, but you'd do that differently from just slapping `async` on `componentWillMount`. In any case, frankly, coming back two months later and posting an answer that just tweaks the existing one without atttribution isn't cool. – T.J. Crowder Dec 18 '17 at 08:42
0

 async fetchCatFacts() {
    await axios.get("//localhost:8082/api_v1/orders", {})
        .then((response) => {

          this.catFacts = response.data.data;
          console.log("resp", response.data);
        });