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 <DataPresenter data={data} />.

¿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.