Use Async/Await con Axios en React.js

7 minutos de lectura

Avatar de usuario de Singh
Singh

Siguiendo

Cómo usar async/await con axios en reaccionar

Estoy tratando de hacer una solicitud de obtención simple a mi servidor usando Async/Await en una aplicación React.js. El servidor carga un JSON simple en /data que se parece a esto

JSON

{
   id: 1,
   name: "Aditya"
}

Puedo obtener los datos en mi aplicación React utilizando el método jquery ajax get simple. Sin embargo, quiero utilizar la biblioteca axios y Async/Await para seguir los estándares ES7. Mi código actual se ve así:

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

Usando este enfoque me sale el siguiente error:

Los objetos no son válidos como un niño React (encontrado: [object Promise]). Si tenía la intención de representar una colección de niños, use una matriz en su lugar.

¿No lo estoy implementando correctamente?

  • render() funciona bien, ya que mencioné claramente que puedo obtener detalles cuando uso $.ajax(). ¿Qué código adicional debo agregar? Esta es una simple solicitud de obtención al servidor que utiliza los estándares ES7.

    – Singh

    13 oct 2017 a las 15:38


  • Idealmente, actualice su pregunta con un ejecutable ejemplo mínimo reproducible que demuestra el problema utilizando un marcador de posición para el ajax (por ejemplo, setTimeout o similar), usando Stack Snippets (el [<>] botón de la barra de herramientas). Stack Snippets admite React, incluido JSX; aquí está cómo hacer uno.

    –TJ Crowder

    13 oct 2017 a las 15:43

  • … pero el código agregado aclara absolutamente las cosas. 🙂

    –TJ Crowder

    13 oct 2017 a las 15:48

  • para tu información, async/await es parte de ES2017, no ES7 (ES2016).

    – Félix Kling

    13 oct 2017 a las 17:20

  • Gracias por la información.

    – Singh

    13 oct 2017 a las 17:24

Avatar de usuario de TJ Crowder
TJ Crowder

Dos cuestiones saltan a la vista:

  1. Su getData nunca devuelve nada, por lo que su promesa (async funciones siempre devuelven una promesa) se cumplirá con undefined si no rechaza

  2. El mensaje de error muestra claramente que estás tratando de cumplir directamente la promesa. getData devoluciones, en lugar de esperar a que se liquide y luego devolver el valor de cumplimiento

Direccionamiento #1: getData debería devolver el resultado de llamar json:

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

Dirección #2: Tendríamos que ver más de su código, pero fundamentalmente, no puede hacerlo

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

…porque eso no espera a la resolución. En su lugar, necesitarías usar getData para establecer el estado:

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

… y usa ese estado al renderizar:

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

Actualizar: ahora que nos ha mostrado su código, debe hacer algo como este:

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>
        );
    }
}

Más actualización: Has indicado una preferencia por usar await en componentDidMount más bien que then y catch. Lo harías anidando un async Función IIFE dentro de él y garantizar que la función no pueda lanzar. (componentDidMount en sí mismo no puede ser asyncnada consumirá esa promesa.) Por ejemplo:

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>
        );
    }
}

  • Me dio este error “‘esto’ no está permitido antes de super ()”. Así que agregué super(); justo antes de “this.state = {data: null};” que luego resultó en un nuevo error: “‘getData’ no está definido no-undef”

    – Singh

    13 oct 2017 a las 15:54

  • @Morfsys: no creo que ese sea el mensaje de error exacto. 🙂 Yo dije “algo como esto”. He actualizado lo anterior, faltaba this. en getData.

    –TJ Crowder

    13 oct 2017 a las 15:57

  • .catch(err => { /*…manejar el error…*/}); se ejecuta ahora. Dice que res.json() no es una función.

    – Singh

    13 oct 2017 a las 16:16


  • Solo para tu información… return res.json() tendría que ser return await res.json() en el ejemplo anterior, ¿correcto? Si lo devuelve en la siguiente línea, la línea de retorno se ejecutará de inmediato en lugar de esperar a que la defina const res justo encima de.

    – dave4jr

    9 mayo 2018 a las 22:13

  • @ dave4jr: No, no es necesario, pero puede ser una buena idea desde el punto de vista del mantenimiento del código, gracias. “Si lo devuelve en la siguiente línea, la línea de retorno se ejecutará de inmediato en lugar de esperar…” No, eso es incorrecto. los return la línea no se ejecutará hasta que await axios('/data') resuelve Así que el código sin el await funcionaría, la promesa creada por getData siendo async simplemente sería esclavo del devuelto por res.json(). Pero desde un código de mantenimiento. pers., tienes razón, mejor await – porque sería fácil estropearlo al hacer cambios en getData.

    –TJ Crowder

    10 de mayo de 2018 a las 5:59

Avatar de usuario de Singh
Singh

En mi experiencia de los últimos meses, me he dado cuenta de que la mejor manera de lograr esto es:

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>
  );
 }
}

Si está tratando de hacer una solicitud de publicación en eventos como un clic, llame getData() funcione en el evento y reemplace el contenido de este modo:

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

Además, si está realizando alguna solicitud cuando el componente está a punto de cargarse, simplemente reemplace async getData() con async componentDidMount() y cambie la función de render así:

render(){
 return (
  <div>{this.state.serverResponse}</div>
 )
}

  • Esta es básicamente mi respuesta, reformulada. También: 1. No hagas componentWillMount un async función. React ignorará la promesa devuelta. 2. A menos que res.data es un accesorio que devuelve una promesa, no tiene ningún sentido usar await al acceder a ella.

    –TJ Crowder

    18 de diciembre de 2017 a las 8:13

  • Simplemente he tratado de simplificar la respuesta. Sin ofender, pero creo then y catch no es el último estándar (ES6) de seguimiento con una promesa. Además, res.json() no funcionaba para mí, por lo que tuve que reemplazarlo con res.data que viene junto con una promesa enGET o POST solicitud.

    – Singh

    18 de diciembre de 2017 a las 8:34

  • then y catch son la forma ES2015 (también conocida como “ES6”) de lidiar con las promesas. async/await son la forma ES2017 (“ES8”). Pero solo puedes usar await dentro de un async función y hacer componentWillMount async es crear una promesa que nunca se consumirá. Si quieres usar await en cambio, eso está bien, pero lo harías de manera diferente a simplemente abofetear async en componentWillMount. En cualquier caso, francamente, regresar dos meses después y publicar una respuesta que solo modifica la existente sin atribución no es genial.

    –TJ Crowder

    18 de diciembre de 2017 a las 8:42


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

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

¿Ha sido útil esta solución?