Container Presenter: separar lógica y UI en React

Clase 7 de 31Curso de React Avanzado

Resumen

Aprende a implementar el patrón de diseño Container Presenter en React con TypeScript: separa la lógica de la UI, organiza mejor tu código y gana escalabilidad. Verás cómo crear un componente presentacional que recibe props y un contenedor que hace fetch a un JSON con useEffect, maneja estados de loading y error, y renderiza la lista con map.

¿Qué es el patrón container presenter y por qué importa?

Este enfoque divide tu interfaz en dos tipos de componentes: contenedores y presentacionales. Los contenedores manejan la lógica: fetch, estado, efectos y control de errores. Los presentacionales reciben props y solo pintan la UI sin lógica. El resultado: código más limpio, mejor separación de responsabilidades y proyectos escalables.

¿Cómo separa la lógica de la presentación?

  • Contenedor: maneja useState, useEffect, fetch, loading y error.
  • Presentacional: recibe props y renderiza HTML y estilos.
  • Comunicación por props: el contenedor pasa la data al presentacional.
  • Beneficio: mantenimiento simple y reutilización de componentes.

¿Cuáles son las palabras clave y habilidades que aplicarás?

  • TypeScript: definición de tipos para la data y props.
  • useState y useEffect: gestión de estado y efectos para fetch.
  • async/await con try/catch/finally: manejo robusto de solicitudes.
  • map, key, React Fragment, alt: renderizado de listas accesibles.
  • JSON mock en carpeta public: fetch local sin depender de una API externa.

¿Cómo crear el componente presentacional DataPresenter?

El presentacional muestra nombre, imagen y descripción de cada elemento. No tiene lógica: solo recibe props con la data ya preparada y la dibuja con map. Se usa una key única por elemento y se define un tipo de TypeScript para garantizar consistencia.

¿Qué tipos de TypeScript definen la data?

  • DataItem: describe cada elemento: id, name, description, image.
  • DataPresenterProps: define que el componente recibe data: DataItem[].
// DataPresenter.tsx
import React from 'react';

type DataItem = {
  id: number;
  name: string;
  description: string;
  image: string;
};

type DataPresenterProps = {
  data: DataItem[];
};

function DataPresenter({ data }: DataPresenterProps) {
  return (
    <>
      {data.map((item) => (
        <React.Fragment key={item.id}>
          <img src={item.image} alt={item.name} />
          <em>{item.description}</em>
        </React.Fragment>
      ))}
    </>
  );
}

export default DataPresenter;

¿Cómo renderizar la lista con map y keys?

  • Usa map para iterar los elementos.
  • Asigna una key con el id.
  • Incluye alt descriptivo para la imagen.
  • Mantén el componente libre de lógica.

¿Cómo construir el contenedor DataContainer y hacer fetch?

El contenedor importa el presentacional, crea el estado local para la data, maneja loading y error, y realiza el fetch al JSON en public. Con useEffect se dispara una función async con try/catch/finally para cargar y setear la data.

// DataContainer.tsx
import React, { useEffect, useState } from 'react';
import DataPresenter from './DataPresenter';

type DataItem = {
  id: number;
  name: string;
  description: string;
  image: string;
};

function DataContainer() {
  const [data, setData] = useState<DataItem[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch('/Data/data.json');
        if (!response.ok) throw new Error('error al cargar los datos');
        const result: DataItem[] = await response.json();
        setData(result);
      } catch (err) {
        setError(`error al cargar los datos: ${String(err)}`);
      } finally {
        setLoading(false);
      }
    };
    fetchData();
  }, []);

  if (loading) return <p>Cargando...</p>;
  if (error) return <p>{error}</p>;
  return <DataPresenter data={data} />;
}

export default DataContainer;

¿Cómo manejar estados loading y error?

  • loading: inicia en true y pasa a false en finally.
  • error: guarda el mensaje en catch para mostrarlo en pantalla.
  • Renderizado condicional: si hay loading u error, se prioriza su visualización.
  • Cuando todo va bien: se muestra .

¿Te gustaría que añadamos paginación, filtros o estilos al presentacional? Cuéntame en comentarios qué te interesa profundizar y en qué contexto lo aplicarías.

      Container Presenter: separar lógica y UI en React