← Back to list

Dominando React en 2026: APIs, Asincronía y Componentización (Parte 3 de 8)

En el módulo anterior, aprendimos a dotar de memoria a nuestros componentes con useState y a controlar su ciclo de vida con useEffect…

Eddy Ortega · 2026-06-12 18:39 · 0 claps · 3.2 min read
#react #typescript #reactjs #frontend
Open on Medium ↗
Wiki topics: 🌐 · Web Development

Dominando React en 2026: APIs, Asincronía y Componentización (Parte 3 de 8)

En el módulo anterior, aprendimos a dotar de memoria a nuestros componentes con useState y a controlar su ciclo de vida con useEffect usando datos estáticos. Hoy daremos el salto al mundo real. Vamos a conectar nuestra tienda Fake Store a la FakeStoreAPI, manejaremos estados de red (cargas y errores) y, lo más importante, aprenderemos a escalar nuestra aplicación dividiendo la interfaz en múltiples componentes reutilizables a través de las Props.

1. El Reto de la Asincronía en React

En el desarrollo de software, ya sea sirviendo un endpoint en Node, Go, C# o consumiéndolo en el cliente, las peticiones de red toman tiempo. En React, no podemos simplemente detener la ejecución del componente mientras esperamos la respuesta del servidor; la interfaz de usuario debe seguir siendo interactiva.

Para lograr esto, combinamos useEffect con funciones asíncronas (async/await) y la API nativa fetch.

Regla de Oro: La función de callback principal de un useEffect no puede ser asíncrona directamente (no puedes hacer useEffect(async () => {...}) porque React espera que el hook devuelva una función de limpieza síncrona, no una Promesa). La solución es declarar la función asíncrona por dentro y llamarla inmediatamente.

2. Consumiendo la FakeStoreAPI

Vamos a transformar nuestra simulación del módulo anterior en una petición real. FakeStoreAPI nos provee un endpoint perfecto para nuestro E-Commerce: [https://fakestoreapi.com/products](https://fakestoreapi.com/products).

Para construir una interfaz robusta, un componente que hace fetching de datos debe manejar siempre tres estados:

  1. Datos: La información que vamos a renderizar.
  2. Carga (Loading): Un indicador visual mientras la promesa se resuelve.
  3. Error: Retroalimentación si el servidor falla o no hay conexión.

Veamos cómo se implementa este patrón en nuestro App.jsx:

import { useState, useEffect } from 'react';
import './App.css';

function App() {
  const [productos, setProductos] = useState([]);
  const [cargando, setCargando] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // 1. Declaramos la función asíncrona internamente
    const obtenerProductos = async () => {
      try {
        const respuesta = await fetch('https://fakestoreapi.com/products');

        if (!respuesta.ok) {
          throw new Error(`Error HTTP: ${respuesta.status}`);
        }

        const datos = await respuesta.json();
        setProductos(datos);
      } catch (err) {
        setError(err.message);
      } finally {
        // Se ejecuta siempre, falle o no la petición
        setCargando(false); 
      }
    };

    // 2. La invocamos
    obtenerProductos();
  }, []); // Arreglo vacío: solo se ejecuta al montar el componente

  // Renderizados tempranos (Early Returns) para manejar UI de red
  if (cargando) return <div className="loader">Cargando catálogo de Aztekode...</div>;
  if (error) return <div className="error">Hubo un problema: {error}</div>;

  return (
    <div className="main-container">
      <h1>Fake Store</h1>
      <div className="grid-productos">
        {productos.map(producto => (
          <div key={producto.id} className="card-producto">
            <img src={producto.image} alt={producto.title} width="100" />
            <h3>{producto.title}</h3>
            <p>${producto.price}</p>
            <button>Agregar al carrito</button>
          </div>
        ))}
      </div>
    </div>
  );
}

export default App;

3. Divide y Vencerás: Componentización y Props

El código anterior funciona, pero a medida que agreguemos lógica al carrito, botones de favoritos o validaciones de stock, nuestro archivo App.jsx se convertirá en un monolito imposible de mantener.

La filosofía central de React es construir bloques de Lego. Vamos a extraer la lógica visual de un producto individual hacia su propio componente. Para enviarle los datos desde App hasta este nuevo componente, usaremos Props (Propiedades).

Creando el componente ProductoCard

Crea un nuevo archivo en tu proyecto llamado src/ProductoCard.jsx:

// src/ProductoCard.jsx

// Desestructuramos las "props" directamente en los parámetros de la función
export function ProductoCard({ titulo, precio, imagen, categoria }) {
  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>
        <button className="btn-agregar">Agregar al carrito</button>
      </div>
    </div>
  );
}

Nota arquitectónica: Las Props son de solo lectura (inmutables). Un componente hijo (ProductoCard) nunca debe intentar modificar las propiedades que recibe de su padre (App). Si el dato debe cambiar, el padre debe enviarle también una función (un callback) para actualizar el estado superior.

Refactorizando App.jsx

Ahora importamos nuestro nuevo componente y le pasamos los datos necesarios iterando sobre nuestra lista:

import { useState, useEffect } from 'react';
import { ProductoCard } from './ProductoCard'; // Importamos el componente
import './App.css';

function App() {
  const [productos, setProductos] = useState([]);
  const [cargando, setCargando] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // ... misma lógica de fetch anterior ...
  }, []);

  if (cargando) return <div className="loader">Cargando catálogo...</div>;
  if (error) return <div className="error">Hubo un problema: {error}</div>;

  return (
    <div className="main-container">
      <h1>Fake Store</h1>
      <div className="grid-productos">
        {productos.map(producto => (
          // Invocamos el componente hijo y le pasamos Props
          <ProductoCard 
            key={producto.id} // Obligatorio en React al renderizar listas
            titulo={producto.title}
            precio={producto.price}
            imagen={producto.image}
            categoria={producto.category}
          />
        ))}
      </div>
    </div>
  );
}

export default App;

Próximos pasos…

Hemos logrado una arquitectura frontend mucho más limpia, separando la lógica de obtención de datos de la lógica de presentación visual.

Sin embargo, si quisieras que el botón “Agregar al carrito” dentro de ProductoCard actualice un contador global en la barra de navegación de App, nos enfrentaríamos a un nuevo reto: ¿Cómo compartimos el estado entre componentes que están en diferentes niveles?

En el Módulo 4 de 8, exploraremos los patrones de elevación de estado (Lifting State Up) y daremos nuestros primeros pasos creando Custom Hooks para extraer y reutilizar toda esa lógica de llamadas a la API de forma profesional.


메타데이터
post_id
81bd7c376cff
slug
dominando-react-en-2026-apis-asincronía-y-componentización-parte-3-de-8-81bd7c376cff
url
https://medium.com/@atrox39/dominando-react-en-2026-apis-asincron%C3%ADa-y-componentizaci%C3%B3n-parte-3-de-8-81bd7c376cff
canonical_url
https://medium.com/@atrox39/dominando-react-en-2026-apis-asincron%C3%ADa-y-componentizaci%C3%B3n-parte-3-de-8-81bd7c376cff
author_url
https://medium.com/@atrox39
status
ok
fetched_at
2026-06-13 12:55:53