Pasar estado global entre componentes profundamente anidados es uno de los retos más comunes al trabajar con React. Gracias a React Context ya no necesitas enviar props componente por componente, pero la forma de consumir ese contexto también puede simplificarse. Aquí aprenderás a reemplazar el patrón de render props del consumer por el hook useContext, logrando un código mucho más limpio y legible.
¿Por qué reemplazar el consumer con useContext?
Cuando usas TodoContext.Consumer, debes envolver tu JSX dentro de una función que recibe el value del provider. Eso funciona, pero añade una capa extra de indentación y puede dificultar la lectura del componente. El hook React.useContext resuelve exactamente este problema: te permite acceder al estado del provider directamente dentro del cuerpo de tu componente funcional, sin necesidad de render props [0:55].
El cambio consiste en:
- Importar tu contexto (
TodoContext).
- Llamar a
React.useContext(TodoContext) y guardar el resultado en una variable.
- Desestructurar las propiedades que necesitas (
error, loading, searchedTodos, etc.).
- Eliminar por completo el bloque
TodoContext.Consumer.
jsx
import { TodoContext } from '../TodoContext';
function AppUI() {
const {
error,
loading,
searchedTodos,
completeTodo,
deleteTodo,
} = React.useContext(TodoContext);
return (
// Tu JSX directamente, sin consumer
);
}
Esta sintaxis es equivalente a la anterior, pero elimina la función anidada dentro del consumer y deja el componente mucho más plano [1:38].
¿Cómo conectar TodoCounter y TodoSearch con useContext?
Una vez que dominas el patrón, puedes aplicarlo a cualquier componente que antes recibía datos por props.
Conectar el TodoCounter
En lugar de recibir total y completed como propiedades, el componente las obtiene directamente del contexto [3:08]:
jsx
import { TodoContext } from '../TodoContext';
function TodoCounter() {
const { totalTodos, completedTodos } = React.useContext(TodoContext);
return (
<h2>Has completado {completedTodos} de {totalTodos} TODOs</h2>
);
}
Un detalle importante: los nombres de las variables deben coincidir exactamente con los que definiste en el provider. Si en el contexto exportaste totalTodos y en el componente llamas total, el valor será undefined y la interfaz no mostrará la información correcta [3:42].
Conectar el TodoSearch
El mismo procedimiento aplica para el buscador. Se eliminan las props y se desestructura el contexto [4:30]:
jsx
import { TodoContext } from '../TodoContext';
function TodoSearch() {
const { searchValue, setSearchValue } = React.useContext(TodoContext);
return (
<input
value={searchValue}
onChange={(e) => setSearchValue(e.target.value)}
/>
);
}
Al guardar y recargar, el filtrado de TODOs funciona de nuevo: escribir en el campo de búsqueda oculta los elementos que no coinciden con el texto ingresado.
¿Qué ventajas ofrece useContext frente al consumer?
Ambas formas logran el mismo resultado, pero useContext presenta beneficios claros:
- Menos indentación: el JSX no queda envuelto en una función extra.
- Lectura más directa: las variables del contexto se declaran al inicio del componente, igual que un
useState o useEffect.
- Consistencia con otros hooks: si ya usas
useState, useEffect o custom hooks como useLocalStorage, agregar useContext mantiene el mismo patrón [0:42].
- Facilidad de refactorización: al cambiar el nombre de una propiedad en el provider, solo necesitas actualizar la desestructuración en cada componente.
El flujo general queda así: el provider envuelve la aplicación y expone el estado global, mientras que cada componente hijo, sin importar su nivel de anidación, llama a useContext para consumir únicamente las propiedades que necesita. No más prop drilling, no más render props innecesarias.
Si ya lograste conectar tus componentes con useContext, el siguiente paso natural es construir el componente modal con el formulario para crear nuevos TODOs. Cuéntanos cómo te fue con la migración y qué diferencias notaste en la legibilidad de tu código.