← Back to list

Dominando React en 2026: Elevación de Estado y Custom Hooks (Parte 4 de 8)

En el módulo anterior, conectamos nuestra tienda a una API real y separamos la interfaz en componentes más pequeños con Props. Pero surgió…

Eddy Ortega · 2026-06-15 16:55 · 0 claps · 3.6 min read
#react #reactjs #typescript #javascript #frontend
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Dominando React en 2026: Elevación de Estado y Custom Hooks (Parte 4 de 8)

En el módulo anterior, conectamos nuestra tienda a una API real y separamos la interfaz en componentes más pequeños con Props. Pero surgió un problema arquitectónico: si el botón de compra vive dentro del componente hijo (ProductoCard), ¿cómo le avisamos al componente padre (App) que debe actualizar el carrito de compras? En esta cuarta entrega, resolveremos este desafío de comunicación utilizando la "Elevación de Estado" y, para coronar, limpiaremos nuestro código creando nuestro primer Custom Hook.

1. El Flujo de Datos Unidireccional en React

Una de las reglas más estrictas de React es que los datos fluyen hacia abajo (Top-Down). Un componente padre puede pasarle datos a su hijo a través de las Props, pero un hijo no puede modificar directamente el estado del padre, ni puede pasarle datos hacia arriba de forma natural.

Entonces, ¿cómo hacemos que un clic en ProductoCard altere un contador en App?

La solución es un patrón llamado Elevación de Estado (Lifting State Up). Si un hijo necesita alterar el estado del padre, el padre debe pasarle una función actualizadora (un callback) a través de las Props. El hijo simplemente ejecuta esa función cuando ocurre el evento.

2. Implementando el Carrito de Compras

Vamos a modificar nuestros componentes para aplicar este patrón. Primero, actualizaremos nuestro componente hijo para que reciba y ejecute la acción.

Paso 1: Actualizar ProductoCard.jsx

Añadiremos una nueva prop llamada onAgregar. Nota cómo la función onClick del botón ahora simplemente invoca esta prop.

// src/ProductoCard.jsx

export function ProductoCard({ id, titulo, precio, imagen, categoria, onAgregar }) {
  return (
    <div className="card-producto">
      <span className="badge-categoria">{categoria}</span>
      <img src={imagen} alt={titulo} />

      <div className="card-body">
        <h3>{titulo}</h3>
        <p className="precio">${precio.toFixed(2)}</p>
        {/* Ejecutamos la función que viene del padre, pasando el ID del producto */}
        <button 
          className="btn-agregar" 
          onClick={() => onAgregar({ id, titulo, precio })}
        >
          Agregar al carrito
        </button>
      </div>
    </div>
  );
}

Paso 2: Elevar el estado en App.jsx

Ahora, en nuestro componente principal, crearemos el estado del carrito y la función que dictará qué sucede cuando se hace clic en un producto.

// Fragmento de src/App.jsx
// ... (lógica de fetch anterior)

const [carrito, setCarrito] = useState([]);

// Esta es la función que pasaremos hacia abajo
const manejarAgregarAlCarrito = (producto) => {
  // Usamos el estado previo para no perder lo que ya estaba en el carrito
  setCarrito((carritoActual) => [...carritoActual, producto]);
};

// En el return, mostramos el contador y pasamos la función al hijo
return (
  <div className="main-container">
    <header className="navbar">
      <h1>Fake Store</h1>
      <div className="carrito-icono">
        Artículos: {carrito.length}
      </div>
    </header>

    <div className="grid-productos">
      {productos.map(producto => (
        <ProductoCard 
          key={producto.id}
          id={producto.id}
          titulo={producto.title}
          precio={producto.price}
          imagen={producto.image}
          categoria={producto.category}
          onAgregar={manejarAgregarAlCarrito} // Pasamos la función como prop
        />
      ))}
    </div>
  </div>
);

¡Magia! Ahora, cada vez que el hijo ejecuta onAgregar, en realidad está ejecutando manejarAgregarAlCarrito en el padre, y la interfaz se actualiza correctamente.

3. Abstracción y Arquitectura: Custom Hooks

Nuestro App.jsx ahora tiene un problema diferente: está mezclando la lógica de UI (el carrito) con la lógica de obtención de datos de red (el fetch enorme del módulo 3). En la ingeniería de software moderna, esto viola el Principio de Responsabilidad Única.

React nos permite extraer la lógica de los Hooks nativos (useState, useEffect) hacia nuestras propias funciones reutilizables. A esto se le llama Custom Hooks.

Un Custom Hook es simplemente una función de JavaScript que:

  1. Empieza con la palabra use (por convención y para que React le aplique reglas de linter).
  2. Utiliza otros Hooks en su interior.

Creando useFetch.js

Vamos a crear un nuevo archivo src/hooks/useFetch.js y a mover toda la lógica de la API allí:

// src/hooks/useFetch.js
import { useState, useEffect } from 'react';

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

  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const respuesta = await fetch(url);
        if (!respuesta.ok) throw new Error(`Error: ${respuesta.status}`);
        const json = await respuesta.json();
        setData(json);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]); // Si la URL cambia, el efecto se vuelve a ejecutar

  // Retornamos un objeto con los estados
  return { data, loading, error };
}

4. El E-Commerce Refactorizado

Con nuestro Custom Hook listo, observa lo limpio, declarativo y profesional que queda nuestro componente raíz App.jsx. Hemos ocultado la complejidad de la red y nos hemos centrado en la vista.

// src/App.jsx
import { useState } from 'react';
import { ProductoCard } from './ProductoCard';
import { useFetch } from './hooks/useFetch';
import './App.css';

function App() {
  // 1. Usamos nuestro Custom Hook
  const { data: productos, loading, error } = useFetch('https://fakestoreapi.com/products');

  // 2. Manejamos el estado local de la vista
  const [carrito, setCarrito] = useState([]);

  const manejarAgregarAlCarrito = (producto) => {
    setCarrito((prev) => [...prev, producto]);
  };

  // 3. Renderizados condicionales
  if (loading) return <div className="loader">Cargando catálogo...</div>;
  if (error) return <div className="error">Ocurrió un error: {error}</div>;

  // 4. Renderizado principal
  return (
    <div className="main-container">
      <header className="navbar">
        <h1>Fake Store</h1>
        <div className="carrito-icono">🛒 {carrito.length}</div>
      </header>

      <div className="grid-productos">
        {productos.map(producto => (
          <ProductoCard 
            key={producto.id}
            {...producto} // Un atajo avanzado para pasar todas las propiedades si los nombres coinciden
            onAgregar={manejarAgregarAlCarrito}
          />
        ))}
      </div>
    </div>
  );
}

export default App;

Próximos pasos…

Nuestra aplicación ya se comunica, maneja su propio estado y abstrae la lógica de red como lo hacen los profesionales. Pero en una tienda real, no vemos todo en una sola pantalla. Necesitamos una página de inicio, una página de detalles del producto y una página para ver el carrito.

En el Módulo 5 de 8, integraremos React Router. Aprenderemos a crear navegación real sin recargar la página (Single Page Application) y a pasar parámetros a través de la URL.


메타데이터
post_id
18ba61db35fc
slug
dominando-react-en-2026-elevación-de-estado-y-custom-hooks-parte-4-de-8-18ba61db35fc
url
https://medium.com/@atrox39/dominando-react-en-2026-elevaci%C3%B3n-de-estado-y-custom-hooks-parte-4-de-8-18ba61db35fc
canonical_url
https://medium.com/@atrox39/dominando-react-en-2026-elevaci%C3%B3n-de-estado-y-custom-hooks-parte-4-de-8-18ba61db35fc
author_url
https://medium.com/@atrox39
status
ok
fetched_at
2026-06-16 19:09:56