Gestión de Estado Complejo con useReducer en React

Clase 15 de 31Curso de React Avanzado

Contenido del curso

Patrones de renderizado y composición

Manejo del estado en React

Resumen

Cuando el estado de un componente en React necesita soportar operaciones como agregar y eliminar elementos, el hook useState puede quedarse corto. Para estos escenarios existe useReducer, un hook que permite gestionar estados más complejos de forma estructurada, separando la lógica en tres actores claros: estado, acciones y reducers. A continuación se desglosa este patrón aplicado a un proyecto práctico: un emoji todo list construido con Vite, React y TypeScript.

¿Cuáles son los tres actores del manejo de estado con useReducer?

El patrón que propone useReducer se sostiene sobre tres pilares que se comunican en un flujo unidireccional [01:42]:

  • Estado (state): es la estructura donde se almacena toda la información. Funciona como una bolsa o maleta que se va llenando con datos. El componente únicamente lee el estado para renderizar la interfaz.
  • Acciones (actions): representan qué se quiere hacer. En el ejemplo se definen dos acciones: add_todo para agregar un elemento y remove_todo para eliminarlo.
  • Reducer: es la función que indica cómo se modifica el estado. Es el único actor autorizado para cambiar los datos; ni el componente ni la acción pueden hacerlo directamente.

¿Cómo se comunican estos actores entre sí?

El flujo funciona así [03:15]: el componente detecta que necesita una modificación, por ejemplo agregar un emoji. En lugar de alterar el estado, despacha una acción indicando el tipo de operación. Esa acción viaja hacia el reducer, que es quien ejecuta la lógica correspondiente y devuelve un nuevo estado. Finalmente, el componente vuelve a leer ese estado actualizado y pinta los cambios en la UI.

Este ciclo unidireccional garantiza previsibilidad: siempre se sabe dónde ocurre cada cosa.

¿Cómo se estructura el proyecto del emoji todo list?

El ejercicio consiste en un input de texto donde el usuario escribe una palabra —por ejemplo eat, sleep o exercise— y al presionar Enter se convierte en un emoji que se agrega a una lista [00:40]. Al hacer clic sobre cualquier emoji de la lista, este se elimina, simulando que la tarea fue completada.

¿Cómo crear el proyecto con Vite y TypeScript?

Para iniciar el proyecto se ejecuta [05:10]:

bash npm create vite@latest MyUseReducerApp

Se selecciona React con TypeScript, luego:

bash cd MyUseReducerApp npm install npm run dev

El servidor de desarrollo arranca en el puerto 5173. Dentro de src se crea la carpeta components y el archivo todoList.tsx, donde vivirá toda la lógica del componente.

¿Cómo definir tipos, acciones y estado inicial en TypeScript?

Una de las grandes ventajas de TypeScript es que obliga a pensar en la forma de los datos antes de escribir lógica. Se definen tres tipos principales [06:20]:

typescript type Todo = { id: number; text: string; };

type State = { todos: Todo[]; };

type Action = | { type: 'add_todo'; payload: string } | { type: 'remove_todo'; payload: number };

  • Todo: cada tarea lleva un id numérico —necesario para identificar qué elemento eliminar mediante filter— y un text de tipo string con la palabra ingresada.
  • State: contiene un arreglo de Todo. Representa la lista completa de tareas.
  • Action: es un union type que admite dos variantes. Cuando se agrega, el payload es el texto; cuando se elimina, el payload es el id del elemento a remover.

El estado inicial se declara como una constante tipada [08:50]:

typescript const initialState: State = { todos: [], };

Este arreglo vacío es el punto de partida. A medida que el usuario interactúe, el reducer se encargará de devolver nuevas versiones de este estado con los elementos agregados o eliminados.

Con los tipos definidos y el estado inicial listo, el siguiente paso es construir la función reducer y conectar el despacho de acciones desde el componente. Si ya trabajas con useState y sientes que necesitas más estructura, useReducer es la evolución natural para mantener tu código limpio y predecible. ¿Qué otro caso de uso se te ocurre para aplicar este patrón?