Resumen

Pasar propiedades de componente en componente puede volverse un problema real cuando tu aplicación crece. React Context resuelve exactamente eso: permite compartir estado entre cualquier componente sin importar qué tan profundo esté en el árbol, eliminando la necesidad de enviar props desde el abuelo hasta el tataranieto. A continuación se explica paso a paso cómo crear un provider, mover toda la lógica de estado hacia él y consumir esa información con render props.

¿Por qué las props se vuelven un problema en aplicaciones grandes?

Cuando tenemos una estructura de componentes con varios niveles de profundidad, cada nivel intermedio debe recibir y reenviar las props que necesitan los componentes más internos. Esto se conoce informalmente como prop drilling [0:24]. En el ejemplo de la clase, el componente App envía datos a AppUI, y este a su vez los distribuye a TodoCounter, TodoSearch, TodoList y TodoItem [1:07].

  • Con solo tres niveles ya se vuelve incómodo.
  • Si agregáramos un componente TodoHeader que envuelva a TodoCounter y TodoSearch, tendríamos que pasar cuatro propiedades extra solo de paso [1:33].
  • El flujo de datos de ida y bajada se complica rápidamente.

¿Cómo funciona createContext para crear providers y consumers?

React.createContext es la función que genera un objeto con dos componentes: un Provider y un Consumer [2:24]. El Provider envuelve la parte de la aplicación que necesita acceso al estado compartido, mientras que el Consumer permite leer ese estado desde cualquier componente hijo, sin importar la profundidad.

¿Qué es TodoContext y cómo se estructura?

Dentro de una carpeta llamada TodoContext se crea un archivo index.js donde se define el contexto [2:08]:

javascript import React from 'react'; const TodoContext = React.createContext();

Este TodoContext expone TodoContext.Provider y TodoContext.Consumer. Para facilitar su uso, se crea una función componente llamada TodoProvider que actúa como atajo [3:07]:

javascript function TodoProvider(props) { // Aquí va toda la lógica de estado return ( <TodoContext.Provider value={{ totalTodos, completedTodos, searchValue, setSearchValue, searchedTodos, completeTodo, deleteTodo, loading, error, }}> {props.children} </TodoContext.Provider> ); }

La propiedad especial value es donde se colocan todos los datos y métodos que queremos compartir [4:17]. Al usar props.children, cualquier componente que se coloque dentro de <TodoProvider> tendrá acceso al contexto.

¿Cómo se mueve la lógica al contexto?

El custom hook useLocalStorage, que contiene toda la lógica para leer y escribir en localStorage, se extrae a su propio archivo dentro de la carpeta del contexto [4:44]. Luego se importa desde TodoProvider:

javascript import { useLocalStorage } from './useLocalStorage';

Toda la lógica de búsqueda, completar y borrar todos también se traslada al TodoProvider [5:28]. El resultado es un componente App extremadamente limpio que solo necesita envolver a AppUI con el provider [6:30]:

javascript import { TodoProvider } from '../TodoContext';

function App() { return ( <TodoProvider> <AppUI /> </TodoProvider> ); }

¿Qué son las render props y cómo se consume el contexto?

En AppUI ya no se reciben props directamente. En su lugar, se utiliza TodoContext.Consumer con una técnica llamada render props [8:02]. En vez de pasar componentes hijos de forma directa, se envía una función que recibe como parámetro el value del provider:

javascript <TodoContext.Consumer> {({ error, loading, searchedTodos, completeTodo, deleteTodo }) => ( // Aquí va el JSX que usa esas variables )} </TodoContext.Consumer>

  • Los paréntesis en lugar de llaves permiten retornar JSX directamente sin escribir return [8:55].
  • Se usa desestructuración en los parámetros para extraer solo las propiedades necesarias del objeto value [9:10].
  • Al recargar la aplicación, los datos persisten gracias a localStorage y el estado se comparte correctamente [9:52].

Con este enfoque, componentes como TodoCounter y TodoSearch todavía necesitan actualizarse para consumir el contexto por su cuenta. Existe además una forma más sencilla de consumir el Consumer que se aborda en la siguiente sesión. Si quieres profundizar en las render props y sus variantes, comparte en los comentarios qué patrones has usado para manejar estado compartido en tus proyectos.