Use Async/Await with Axios in React.js
Asked Answered
L

4

74

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?

Logan answered 13/10, 2017 at 15:33 Comment(5)
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.Logan
Ideally, update your question with a runnable minimal reproducible example 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.Congratulatory
...but the added code absolutely makes things clear. :-)Congratulatory
FYI, async/await is part of ES2017, not ES7 (ES2016).Broadcasting
Thank you for the information.Logan
C
95

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>
        );
    }
}
Congratulatory answered 13/10, 2017 at 15:42 Comment(8)
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"Logan
@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.Congratulatory
.catch(err => { /*...handle the error...*/}); executes now.It says res.json() is not a function.Logan
@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).Congratulatory
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.Confirmand
@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.Congratulatory
@T.J.Crowder Ahh yes you are correct, thanks for the explanation sir.Confirmand
NOTE: In case of error --- e.response.data --- will have the actual error.Oriya
L
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>
 )
}
Logan answered 18/12, 2017 at 7:57 Comment(3)
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.Congratulatory
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 inGET or POST request.Logan
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.Congratulatory
V
0

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

          this.catFacts = response.data.data;
          console.log("resp", response.data);
        });
Vyatka answered 28/9, 2022 at 11:54 Comment(0)
P
0

 const datasend = async () => {
    await axios.post(
      "http://localhost:5000/",
      {
        name: named,
        age: aged,
      },
      (response) => {
        console.log(response);
      }
    );
    console.log("data send");
  };
Pun answered 10/10, 2023 at 16:45 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Pippa

© 2022 - 2024 — McMap. All rights reserved.