10 - Peticiones Ajax en React

Cuando necesitamos hacer peticiones a un servidor web podemos utilizar el método global fetch() de Javascript. Nos permite obtener en forma asíncrona recursos de un servidor web.

Problema

Confeccionar una aplicación que recupere una respuesta en JSON de la dirección:

http://scratchya.com.ar/vue/datos.php

La estructura del archivo JSON es:

[
  {
    "codigo": 1,
    "descripcion": "papas",
    "precio": 12.33
  },
  {
    "codigo": 2,
    "descripcion": "manzanas",
    "precio": 54
  }
]

Luego de recuperar los datos mostrarlos en una tabla HTML

El primer paso será crear el proyecto008 desde la línea de comandos de Node.js mediante la aplicación create-react-app

App.js

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      articulos: []
    }
  }

  componentWillMount() {
    fetch('http://scratchya.com.ar/vue/datos.php')
      .then((response) => {
        return response.json()
      })
      .then((art) => {
        this.setState({ articulos: art })
      })    
  }

  render() {
    return (
      <div>
        <table border="1">
        <thead>
          <tr>
            <th>Código</th>
            <th>Descripción</th>
            <th>Precio</th>                    
          </tr>
        </thead>
        <tbody>  
          {this.state.articulos.map(art => {
            return (
              <tr key={art.codigo}>
                <td>{art.codigo}</td>
                <td>{art.descripcion}</td>
                <td>{art.precio}</td>
              </tr>
            );
          })}
        </tbody>
        </table>
      </div>
    );
  }
   
}

export default App;

El método componentWillMount() se ejecuta en forma automática previo a generarse el DOM y tiene por objetivo recuperar los datos del servidor y almacenarlos en el 'estado' de la componente para que luego se muestren en el método render():

  componentWillMount() {
    fetch('http://scratchya.com.ar/vue/datos.php')
      .then((response) => {
        return response.json()
      })
      .then((art) => {
        this.setState({ articulos: art })
      })    
  }

En el constructor definimos en la propiedad 'state' un vector vacío que se modifica cuando llegan los datos del servidor web:

  constructor(props) {
    super(props);
    this.state = {
      articulos: []
    }
  }

El método render() muestra la cabecera de la tabla HTML y mediante el recorrido del vector almacenado en el 'state' se muestran los artículos recuperados del servidor:

  render() {
    return (
      <div>
        <table border="1">
        <thead>
          <tr>
            <th>Código</th>
            <th>Descripción</th>
            <th>Precio</th>                    
          </tr>
        </thead>
        <tbody>  
          {this.state.articulos.map(art => {
            return (
              <tr>
                <td>{art.codigo}</td>
                <td>{art.descripcion}</td>
                <td>{art.precio}</td>
              </tr>
            );
          })}
        </tbody>
        </table>
      </div>
    );
  }

Cuando ejecutamos la aplicación tenemos como resultado:

peticiones fetch react

Cuando hay peticiones a un servidor puede haber un tiempo de espera hasta que se recuperan los datos. Vamos a modificar el ejercicio anterior para que se muestre un mensaje hasta que lleguen los datos del servidor:

import React, { Component } from 'react';
import './App.css';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      articulos: [],
      recuperado: false
    }
  }

  componentWillMount() {
    fetch('http://scratchya.com.ar/vue/datos.php')
      .then((response) => {
        return response.json()
      })
      .then((art) => {
        this.setState({ 
                        articulos: art,
                        recuperado: true
                     })
      })    
  }

  render() {
    if (this.state.recuperado)
      return this.mostrarTabla()
    else
      return (<div>recuperando datos...</div>)
  }

  mostrarTabla() {
    return (
      <div>
        <table border="1">
        <thead>
          <tr>
            <th>Código</th>
            <th>Descripción</th>
            <th>Precio</th>                    
          </tr>
        </thead>
        <tbody>  
          {this.state.articulos.map(art => {
            return (
              <tr key={art.codigo}>
                <td>{art.codigo}</td>
                <td>{art.descripcion}</td>
                <td>{art.precio}</td>
              </tr>
            );
          })}
        </tbody>
        </table>
      </div>
    );
  }
   
}

export default App;

Hemos guardado en el 'estado' de la componente además del vector con los datos de artículos una bandera llamada 'recuperado' con el valor falso:

    this.state = {
      articulos: [],
      recuperado: false
    }

Cuando realmente tenemos todos los datos recuperados procedemos a modificar el 'estado' de la componente (guardamos los artículos recuperados y cambiamos el estado de la bandera):

  componentWillMount() {
    fetch('http://scratchya.com.ar/vue/datos.php')
      .then((response) => {
        return response.json()
      })
      .then((art) => {
        this.setState({ 
                        articulos: art,
                        recuperado: true
                     })
      })    
  }

En el método render() según el 'estado' de la bandera 'recuperado' procedemos a mostrar la tabla HTML o un div con un mensaje de información:

  render() {
    if (this.state.recuperado)
      return this.mostrarTabla()
    else
      return (<div>recuperando datos...</div>)
  }

Posiblemente el tiempo de respuesta en este problema es muy pequeño y no alcancemos a ver el mensaje por pantalla, podemos modificar temporalmente el programa y hacer que espere 2 segundos antes de recuperar los datos (esto es solo a modo de prueba y no tiene sentido en este problema esperar):

  componentWillMount() {
    fetch('http://scratchya.com.ar/vue/datos.php')
      .then((response) => {
        return response.json()
      })
      .then((art) => {
        setTimeout( () => {this.setState({ 
                        articulos: art,
                        recuperado: true
                     })}, 2000);
      })    
  }

Con esto tenemos la oportunidad de ver el mensaje:

fetch react