React, parte 5: Consumir api

Para completar el siguiente ejercicio, vamos a replicar lo que ya habíamos hecho en una publicación pasada: https://fernando-gaitan.com.ar/javascript-parte-13-ajax/ Pero con código de React.

Vamos a crear dos componentes: PostsList.jsx y PostItem.jsx.

El componente PostsList.jsx tendrá el siguiente código:

export const PostsList = () => {

    return (
        <form>
            <h1>Ejemplo de API</h1>
            <select className="form-select mb-3">
                <option value=""> Mostrar todo </option>
                <option value="completas"> Mostrar completas </option>
                <option value="incompletas"> Mostrar incompletas </option>
            </select>
            <button type="button" className="btn btn-primary mb-3"> Mostrar lista de tareas </button>        
            <ul className="list-group">
            </ul>
        </form>
    )

}

Lo agregamos al componente principal main.jsx:

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import 'bootstrap/dist/css/bootstrap.min.css'
import { PostsList } from './components/PostsList'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <div className='container mt-3'>
      <PostsList />
    </div>
  </React.StrictMode>,
)

Analizamos el funcionamiento de PostsList.jsx: lo que queremos hacer, es que al presionar el botón «Mostrar lista de tareas» nos muestre un resultado dependiendo de lo seleccionado en el combo:

<select className="form-select mb-3">
     <option value=""> Mostrar todo </option>
     <option value="completas"> Mostrar completas </option>
     <option value="incompletas"> Mostrar incompletas </option>
 </select>

Para ello vamos a necesitar de dos estados: uno para guardar la opción seleccionada del combo (el filtro) y otro para guardar la información que nos devuelva el API

Importamos el hook useState:

import { useState } from "react"

Los estados:

//Opciones del combo.
const [filtro, setFiltro] = useState("");
//La información que me devolverá el API.
const [lista, setLista] = useState([]);

Vamos a modificar el combo de la siguiente forma:

<select className="form-select mb-3" value={filtro} onChange={e => setFiltro(e.target.value)}>
   <option value=""> Mostrar todo </option>
   <option value="completas"> Mostrar completas </option>
   <option value="incompletas"> Mostrar incompletas </option>
</select>

Y a agregar la función que va a llamar al API:

const mostrarTareas = async () => {
    let url = 'https://jsonplaceholder.typicode.com/todos';
    if(filtro == "completas"){
        url += '?completed=true';
    }else if(filtro == "incompletas"){
        url += '?completed=false';
    }
    try{
        let request = await fetch(url);
        let response = await request.json();
        console.log(response);
        setLista(response);
    }catch(e){
        alert("Error al intentar recuperar las tareas");
    }
}

Agregamos el evento onClick al botón:

<button onClick={mostrarTareas} type="button" className="btn btn-primary mb-3"> Mostrar lista de tareas </button>

Por último vamos a editar el componente PostItem.jsx para poder representar cada uno de los POST que devuelve el API:

export const PostItem = ( {title, completed} ) => {

    const getClass = () => {
        return (completed) ? "list-group-item list-group-item-success" : "list-group-item list-group-item-danger";
    }
    
    return (
        <li className={getClass()}>
            {title}
        </li>
    )

}

Finalmente el código PostsList.jsx quedará así:

import { useState } from "react"
import { PostItem } from "./PostItem";

export const PostsList = () => {

    //Opciones del combo.
    const [filtro, setFiltro] = useState("");
    //La información que me devolverá el API.
    const [lista, setLista] = useState([]);

    const mostrarTareas = async () => {
        let url = 'https://jsonplaceholder.typicode.com/todos';
        if(filtro == "completas"){
            url += '?completed=true';
        }else if(filtro == "incompletas"){
            url += '?completed=false';
        }
        try{
            let request = await fetch(url);
            let response = await request.json();
            console.log(response);
            setLista(response);
        }catch(e){
            alert("Error al intentar recuperar las tareas");
        }
    }

    return (
        <form>
            <h1>Ejemplo de listas</h1>
            <select className="form-select mb-3" value={filtro} onChange={e => setFiltro(e.target.value)}>
                <option value=""> Mostrar todo </option>
                <option value="completas"> Mostrar completas </option>
                <option value="incompletas"> Mostrar incompletas </option>
            </select>
            <button onClick={mostrarTareas} type="button" className="btn btn-primary mb-3"> Mostrar lista de tareas </button>
            <ul className="list-group">
                {
                    lista.map((item) => (
                        <PostItem key={item.id} title={item.title} completed={item.completed} />
                    ))
                }
            </ul>
        </form>
    )

}

useEffect

Es un hook que se carga al igual que useState:

import { useEffect, useState } from "react"

Sirve para ejecutar código una vez que se crea el componente.

Recibe dos argumentos: La función que se ejecuta y las dependencias. Si alguna de estas última se modifica se volverá a ejecutar el código que se pasa en el primer argumento:

useEffect(() => {
    mostrarTareas();
}, [])

Al inicio se llamará a la función mostrarTareas().

Podríamos agregarle como dependencia el filtro, y esto hará que cada vez que cambie el valor de éste, se ejecutará nuevamente el código sin necesidad de que pulsemos en el botón:

useEffect(() => {
    mostrarTareas();
}, [filtro])