Hook useFetch en React: La Solución Definitiva para Consumir APIs

Consumir APIs es una tarea común en cualquier aplicación React. Sin embargo, manejar estados como loading, error, o incluso reintentos puede volverse complicado y repetitivo. En este post, aprenderás cómo usar un hook llamado useFetch que simplifica este proceso.

¿Qué es el Hook useFetch?

useFetch es un custom hook de React diseñado para simplificar las solicitudes HTTP a APIs. Este hook encapsula toda la lógica necesaria para manejar solicitudes, respuestas y errores, permitiendo que tus componentes sean más limpios y fáciles de leer.

  Ventajas del Hook useFetch

  • Simplifica la gestión de estados (loading, error y data).
  • Reutilizable en cualquier componente.
  • Opcionalmente, admite reintentos o intervalos de actualización.
  • Menos código repetido en tus componentes.
  • Facilita pruebas unitarias, ya que encapsula la lógica en un solo lugar.
  • Mejora la legibilidad y el mantenimiento del código.

Implementación del Hook useFetch


1. Ejemplo de Uso Básico

jsx
function MiComponente() {
const { data, loading, error, reload } = useFetch('https://api.vercel.app/blog');

if (loading) return <p>Cargando...</p>;
if (error) return <p>Error: {error.message}</p>;

return (
  <div>
    {data && <pre>{JSON.stringify(data)}</pre>}
    <button onClick={reload}>Recargar Datos</button>
  </div>
);
}
  Explicación

El componente MiComponente usa un hook useFetch que además de gestionar estados como data, loading y error, incluye una función reload para volver a realizar la solicitud. Muestra un mensaje mientras los datos se cargan (loading) o un error si ocurre algo mal. Una vez que los datos están disponibles (data), los muestra formateados con JSON.stringify y ofrece un botón que permite recargar los datos llamando a la función reload.

2. Ejemplo de Uso Hook useFetch

jsx
import { useState, useEffect } from 'react';

export function useFetch(url, options) {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
  const fetchData = async () => {
    setIsLoading(true);
    setError(null);

    try {
      const response = await fetch(url, options);
      if (!response.ok) throw new Error(`Error: ${response.status}`);
      const result = await response.json();
      setData(result);
    } catch (err) {
      setError(err);
    } finally {
      setIsLoading(false);
    }
  };

  if (url) fetchData();
}, [url, options]);

return { data, isLoading, error };
}
  Explicación

Este hook personalizado useFetch realiza solicitudes HTTP de forma sencilla. Usa useState para gestionar el estado de los datos (data), la carga (isLoading) y los errores (error). Dentro de un useEffect, define una función asíncrona que realiza la solicitud con fetch, manejando posibles errores y actualizando los estados según el resultado. Finalmente, retorna los estados para que el componente que lo use acceda a la información.

Personalización del Hook useFetch

Puedes agregar funcionalidades extra como reintentos o control de intervalos:

Con reintentos:

jsx
export function useFetchWithRetry(url, options, retries = 3) {
const { data, isLoading, error } = useFetch(url, options);

useEffect(() => {
  let attempts = 0;
  const retryFetch = () => {
    if (error && attempts < retries) {
      attempts++;
      useFetch(url, options);
    }
  };
  retryFetch();
}, [url, options, error, retries]);

return { data, isLoading, error };
}
  Explicación

El hook useFetchWithRetry extiende useFetch añadiendo lógica para reintentar solicitudes fallidas. Usa useEffect para controlar reintentos, incrementando un contador (attempts) cada vez que ocurre un error, hasta alcanzar el límite definido por retries. Si no hay error o se alcanzan los intentos máximos, retorna los mismos estados (data, isLoading, error) de useFetch.

Uso con Opciones Personalizadas

jsx
function CreateUser() {
const [userData, setUserData] = useState({});

const { loading, error, fetchData } = useFetch(
  'https://api.example.com/users', 
  {
    method: 'POST',
    body: JSON.stringify(userData)
  }
);

const handleSubmit = () => {
  fetchData()
    .then(response => {
      // Manejar respuesta
    });
};
}
  Explicación

Este ejemplo utiliza useFetch para realizar una solicitud POST a una API y crear un usuario. El estado userData almacena los datos del usuario a enviar, mientras que fetchData es una función del hook que dispara la solicitud. Al llamar a handleSubmit, se ejecuta fetchData con las opciones especificadas (método y cuerpo de la solicitud) y luego se maneja la respuesta en una promesa.

3. Ejemplo Práctico: Lista de Posts

Demuestra cómo usar useFetch en un componente funcional.

jsx
import React from 'react';
import { useFetch } from './useFetch';

function Posts() {
const { data, isLoading, error } = useFetch('https://jsonplaceholder.typicode.com/posts');

if (isLoading) return <p>Cargando posts...</p>;
if (error) return <p>Error: {error.message}</p>;

return (
  <div>
    <h1>Lista de Posts</h1>
    <ul>
      {data.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  </div>
);
}

export default Posts;
  Explicación

El componente Posts utiliza el hook useFetch para consumir una API que devuelve una lista de publicaciones. Muestra un mensaje de “Cargando” mientras la solicitud está en progreso (isLoading) y un mensaje de error si ocurre un problema. Una vez que los datos están disponibles (data), renderiza un listado de títulos en una lista desordenada.

  Conclusión

useFetch es una herramienta poderosa que simplifica el consumo de APIs, dejando a los desarrolladores más tiempo para concentrarse en la lógica de negocio y en la experiencia del usuario.