Henry Alexander Velásquez Rosas
✨ React Context vs 💀 Props Drilling
Jaime Ramón Santillán Martínez
Muy bien explicado con una sola imagen. Gracias!
React Context resuelve uno de los dolores más comunes al construir aplicaciones con React: el famoso prop drilling. Si trabajas con componentes anidados en varios niveles, esta herramienta te permite crear un estado global y comunicar componentes sin pasar propiedades de padre a hijo a nieto.
Qué es el prop drilling y por qué te complica la vida
Imagina la estructura típica de una app de tareas. Tienes un componente App que llama a TodoCounter, TodoSearch, TodoList y CreateTodoButton. A su vez, TodoList llama a TodoItem. Hasta ahí todo bien, pero la comunicación entre ellos se hace con props, y eso se vuelve un laberinto.
El componente App envía propiedades a TodoCounter, otras distintas a TodoSearch, otras a TodoList, y este último vuelve a pasarlas al TodoItem. Cuando un componente actualiza el estado, ese cambio se propaga hacia arriba y luego baja a otros componentes que ni siquiera son sus hijos directos. Ese flujo enredado es el prop drilling, y en aplicaciones grandes como Platzi, Google o GitHub se vuelve insostenible.
¿Qué es el prop drilling? Es cuando una propiedad atraviesa múltiples niveles de componentes solo para llegar al que realmente la necesita, aunque los intermedios no la usen. [01:32]
Cómo cambia React Context la forma de pensar la app
En lugar de ver tus componentes como un árbol con abuelos, padres e hijos, React Context te invita a verlos como piezas independientes que se conectan directamente al estado global. Cada componente le pide al contexto solo la información que necesita, sin importar en qué nivel esté.
Cómo identificar el problema en tu propio código
En una app real, el componente App suele traer datos desde un custom hook como useLocalStorage, crear estados, definir efectos y actualizadores, y luego entregar todo a un componente como AppUI. El costo de separar lógica e interfaz es que AppUI recibe un montón de props que no usa directamente, sino que reenvía a sus hijos. [04:15]
Por ejemplo, AppUI recibe completedTodos y totalTodos solo para pasárselos a TodoCounter. Recibe searchValue y setSearchValue únicamente para entregárselos a TodoSearch. Esa cadena de reenvíos es el síntoma claro de que necesitas un contexto.
Qué propiedades sí pertenecen al componente y cuáles no
Dentro de AppUI realmente se usan loading, error, searchedTodos, completeTodo y deleteTodo. El resto solo está de paso. Ese filtro mental te ayuda a decidir qué mover al contexto: todo aquello que un componente reenvía sin usar.
Cómo crear un contexto con React.createContext
El primer paso es crear una carpeta llamada TodoContext dentro de src y un archivo index.js. Allí importas React y usas la herramienta React.createContext() para inicializar tu contexto. [09:42]
javascript import React from 'react';
const TodoContext = React.createContext();
Este contexto expone dos componentes internos: TodoContext.Provider y TodoContext.Consumer. El Provider entrega la información, el Consumer la recibe. Puedes usarlos directamente, pero la práctica más limpia es crear tu propio Provider personalizado.
¿Para qué sirve React.createContext? Crea un objeto de contexto que incluye un Provider y un Consumer, los dos componentes con los que compartes y consumes datos globales en tu app. [10:18]
Cómo construir un TodoProvider personalizado
En vez de exportar TodoContext.Provider directamente, defines un componente TodoProvider que envuelve al Provider y encapsula toda la lógica que antes vivía en el componente App.
useLocalStorage al TodoProvider.value del Provider.props.children para insertar cualquier componente dentro.Este patrón te da tres beneficios: un nombre más cómodo sin puntos intermedios, la posibilidad de exportarlo como un componente común, y un lugar único para centralizar la lógica compartida.
javascript function TodoProvider({ children }) { // toda la lógica de estados y actualizadores return ( <TodoContext.Provider value={{ loading, error, searchedTodos, completeTodo, deleteTodo }}> {children} </TodoContext.Provider> ); }
Cómo consumir el contexto con TodoContext.Consumer
Una vez envuelves tu AppUI dentro del TodoProvider en el componente App, cualquier hijo puede acceder al contexto. Para leerlo usas TodoContext.Consumer, que sigue un patrón llamado render props. [16:50]
Esto significa que el Consumer no espera componentes directos como hijos, sino una función que recibe el value del contexto y retorna los elementos a renderizar.
javascript <TodoContext.Consumer> {({ loading, error, searchedTodos, completeTodo, deleteTodo }) => ( <TodoList> {/* render de los todos */} </TodoList> )} </TodoContext.Consumer>
Por qué se llaman render functions
La función que pasas como hijo al Consumer recibe como parámetro el objeto que guardaste en value. Desde ahí desestructuras solo lo que necesitas. Es la forma en que React conecta el dato global con el árbol de componentes sin romper la convención de JSX.
¿Cuándo usar Provider y cuándo Consumer? El Provider envuelve a los componentes que comparten el estado. El Consumer se usa dentro de cualquier componente hijo que quiera leer ese estado.
Qué pasa con TodoCounter y TodoSearch
Al quitar las props a TodoCounter y TodoSearch, esos componentes dejan de funcionar temporalmente. Buscan totalTodos, completedTodos, searchValue y setSearchValue, pero ya no los reciben. La solución es consumir el contexto desde adentro de cada uno.
El TodoContext.Consumer funciona y lo encontrarás en muchos proyectos profesionales de React, pero existe una forma más cómoda de consumir ese contexto sin anidar funciones dentro de funciones. ¿Has usado ya useContext en tus proyectos? Cuéntame en los comentarios cómo organizas tus contextos.
Henry Alexander Velásquez Rosas
✨ React Context vs 💀 Props Drilling
Jaime Ramón Santillán Martínez
Muy bien explicado con una sola imagen. Gracias!
Esteban Augusto Colmenares Blanco
React Context es como una nube donde puedes almacenar funcionalidades, estados, datos, etc. Que necesitan ser compartidos entre diferentes componentes de una aplicación. Digamos que existen varios componentes que necesitan acceder a un mismo estado, como por ejemplo, un estado de carga en lugar de pasar estos datos a través de múltiples niveles de componentes, que conducen al Prop Drilling siendo un patrón que puede ser útil en algunas situaciones, pero también puede resultar incómodo y difícil de mantener si hay muchos niveles de componentes intermedios involucrados. Además, puede hacer que el código sea menos legible y más propenso a errores.
Por ello React Context es una buena solución y acceder a ellos fácilmente desde cualquier lugar de la aplicación. Por ello, otra manera de verlo es como una biblioteca compartida. Imagina que trabajas en una empresa y necesitas compartir una biblioteca de recursos (libros, revistas, etc.) con tus compañeros de trabajo. En lugar de pasar los libros de mano en mano entre los compañeros de trabajo, puedes ponerlos todos en una biblioteca compartida en el edificio de la empresa o en englobarlos en un Google Drive, lo que hace que sea más fácil para todos acceder a los recursos que necesitan.
De manera similar, con React Context, puedes guardar datos que necesitan ser compartidos entre diferentes componentes de tu aplicación en una "biblioteca compartida" (el contexto de React), lo que facilita el acceso a estos datos desde cualquier lugar de tu aplicación sin tener que pasarlos manualmente a través de múltiples niveles de componentes.
Juan David Vanegas Roldán
A ver, yo creo que hubiera sido mejor abordar los aspectos conceptuales y teóricos de react y al final hacer la aplicación. La verdad a estas alturas del curso ya estoy simplemente muy perdido con esa app.
Juan Manuel Balaguera Alvira
El "prop drilling" (también conocido como "props drilling") es un patrón o fenómeno que ocurre en React cuando se pasa una propiedad (prop) a través de múltiples niveles de componentes, aunque los componentes intermedios no necesiten esa propiedad en particular.
En una estructura de componentes anidados, cuando un componente necesita acceder a una prop que se encuentra en un componente padre o abuelo, debe recibir dicha prop como un parámetro y luego pasarla a través de sus propios props a los componentes hijos. A medida que se agregan más componentes en la cadena, este proceso se repite, lo que resulta en un "túnel" o "perforación" de props a través de múltiples niveles de componentes.
El prop drilling puede ocurrir cuando no se utiliza una solución de gestión de estado más avanzada, como Context API o Redux, y se pasa información a través de las props en lugar de compartirla de manera más directa entre los componentes.
El prop drilling puede tener algunas desventajas, como hacer que el código sea más complejo y difícil de mantener, ya que las props deben pasar por varios componentes intermedios incluso si no se utilizan en ellos. También puede dificultar los cambios en la estructura de los componentes, ya que cualquier cambio en la prop que se está pasando requerirá modificar todos los componentes que forman parte del "túnel" de props.
Sin embargo, el prop drilling puede ser una solución adecuada en situaciones simples y pequeñas, y no siempre es problemático. En proyectos más grandes y complejos, se recomienda considerar el uso de patrones de gestión de estado más avanzados, como Context API o Redux, para evitar el prop drilling excesivo y mejorar la eficiencia y la escalabilidad del código.
Raycris Maldonado
Ésta clase es excelente, recomiendo que las vean las veces que sean necesarias para que puedan tener una mejor idea de lo que es React context. Tuve un mejor concepto de como funciona Context después de aquí.
Pero a la misma vez me surge la pregunta, dentro del archivo TodoContext, siempre vamos a tener toda la lógica de nuestro proyecto, como llamados a API, respuestas de las mismas, no sé una lista de productod que necesitamos saber por toda la aplicación y cualquier otra cosa?
Diego Reyes Cabrera
@andrescar18 a dónde tan basado?
Daniel Carpio Robles
Que sufrimiento esta clase!!😔
Fernando Moyano
Dale like si consideras que esta sección de comentarios debería poder esconderse y aparecer a gusto del usuario.
considero que son super útiles pero por momentos interrumpen el foco de concentración del video principal.
Alfonso José Chavarro Cortes
Context permite compartir datos entre componentes. Para crear el contexto es necesario el método
createContext()
Existen dos componentes en el contexto: Provider y Consumer. El Provider es el componente que almacena y proporciona el valor, mientras que el Consumer es el componente que permite consumir los datos.
Existen dos formas de acceder al valor del Provider:
Max Andy Diaz Neyra
Camilo Taborda
Que tal TS ?
Marcelo Vinicio Chavarría Ugalde
TypeScript es un lenguaje de programación súper cool que potencia tus desarrollos al ser más seguro y a prueba de errores en comparación con JavaScript. En resumen, TypeScript se destaca por su tipado estático, capacidad para detectar errores en tiempo de compilación, características avanzadas de tipado y OOP, mientras que JavaScript es más flexible pero puede resultar en errores más difíciles de identificar durante la ejecución.
Aquí te paso documentación https://www.typescriptlang.org/docs/
Aquí te paso un buen video de qué es Ts https://youtu.be/xtp_DuPxo9Q
Jesus Nicolas Vite
Pude arrreglar un error, por mi cuenta leyendo toda la información en inglés!!!
Marcelo Vinicio Chavarría Ugalde
Cool 😎 Pasa esa documentación xd
Frank Sepúlveda
React sera muy famoso, pero la verdad es un caos escribir el codigo.
Angel Bernechea
Nunca antes había estado tan feliz de saber git antes de una clase. Me tomó como 3 horas y 5 ramas poder hacerlo sin quemar troyanos. En serio, sudé mis sesos en esta clase
Jhon Freddy Paz Guzman
voy en la clase 22 y la verdad no entiendo un carajo, ya ni recuerdo cuando fue la ultima clase que entendi algo, espero solamente terminar este curso y que con los demas cursos de react poder entender mejor las cosas
Pablo Zoe Mata Nuñez
x2
Carlos Esteban León Pinilla
Super cool esto de React Context, esto ya estaba antes de Redux o fue después que salió?
Juan Castro
ProfesorCreo que esta lectura puede aclarar un poco esa duda: https://platzi.com/blog/redux-no-esta-muerto/
David Rangel
Les dejo la documentación de este componente de React:
Diego Eduardo Téllez Contreras
Hace poco estuve trabajando con Redux para crear estados globales y sin duda, es mucho más facil hacerlo con React Context. Profe Juan, cómo se cuales son las diferencias entre estos dos metodos y cuando debo usar cada uno?
Juan Castro
ProfesorDiego Eduardo Téllez Contreras
Gracias profe
David Saavedra Vázquez
En 2 mins explicación de Context:
Diego J. Ojeda
Las explicaciones de Juan DC me dejan así 🤯 de lo fácil que hace entender los conceptos
Ricardo Alfonso Chavez Vilcapoma
No entendí un poco el sentido de Provider y Consumer...si alguien puede ayudarme sería bueno. Gracias.
Juan Castro
ProfesorPoder transmitir información de un componente a otro sin necesidad de pasar por promps entre cada componente intermedio
Ricardo Alfonso Chavez Vilcapoma
Gracias profesor...al final si pude entender cómo funciona ello luego de salir a tomar aire fresco 😅.
Aqui les dejo mi código para los valientes que trabajan el proyecto en TS:
import { FC } from 'react' import { AppUI } from './AppUI' import { TodoProvider } from '../TodoContext' const App:FC = () => { return ( <TodoProvider> <AppUI /> </TodoProvider> ) } export default App
import { Dispatch, FC, ReactNode, SetStateAction, createContext, useState } from "react"; import { useLocalStorege } from "./useLocalStorage"; export interface ITodo { text:string, completed:boolean } interface Props { children: ReactNode } interface GlobalState { searchedTodos: ITodo[] completedTodos: number, totalTodos: number, searchValue: string, setSearchValue: Dispatch<SetStateAction<string>> completeTodo: (v:string)=>void, deleteTodo: (v:string)=>void, loading: boolean, error: boolean } const TodoContext = createContext<GlobalState>({} as GlobalState); const TodoProvider:FC<Props> = ({ children }) => { const { item: todos, saveTodos, loading, error, } = useLocalStorege<ITodo[]>('TODOS_V1', []); const completedTodos:number = todos.filter(todo=>todo.completed).length; const totalTodos:number = todos.length; const [searchValue, setSearchValue] = useState<string>(''); const searchedTodos = todos.filter(todo=> todo.text.toLowerCase().includes(searchValue.toLowerCase())); const completeTodo = (text: string) =>{ const newTodos = todos.map(todo=>{ if(todo.text === text) todo.completed = !todo.completed; return todo; }) saveTodos(newTodos); } const deleteTodo = (text: string) => { const newTodos = todos.filter(todo=>todo.text !== text); saveTodos(newTodos); } return ( <TodoContext.Provider value={{ searchedTodos, completedTodos, totalTodos, searchValue, setSearchValue, completeTodo, deleteTodo, loading, error }}> {children} </TodoContext.Provider> ) } export { TodoContext, TodoProvider }
import { useEffect, useState } from "react"; const useLocalStorege = <T,>(itemName:string, initialValue:T) => { const [item, setItem] = useState<T>(initialValue); const [loading, setLoading] = useState(true); const [error, setError] = useState(false); useEffect(() => { setTimeout(() => { try { const localStorageTodos = localStorage.getItem(itemName); let parsedItem:T; if(!localStorageTodos){ localStorage.setItem(itemName, JSON.stringify(initialValue)) parsedItem = initialValue } else { parsedItem = JSON.parse(localStorageTodos) as T; } saveTodos(parsedItem); setLoading(false); } catch (err) { setError(true); } }, 2000); }, []) const saveTodos = (newTodos:T) => { setItem(newTodos); localStorage.setItem(itemName, JSON.stringify(newTodos)) } return { item, saveTodos, loading, error, } } export { useLocalStorege }
import { FC } from "react" import { TodoButton } from "../TodoButton" import { TodoCounter } from "../TodoCounter" import { TodoItem } from "../TodoItem" import { TodoList } from "../TodoList" import { TodoSearch } from "../TodoSearch" import { TodosError } from "../TodoError" import { EmptyTodos } from "../EmptyTodo" import { TodosLoading } from "../TodoLoading" import { TodoContext } from "../TodoContext" // interface Props { // searchedTodos: ITodo[] // completedTodos: number, // totalTodos: number, // searchValue: string, // setSearchValue: Dispatch<SetStateAction<string>> // completeTodo: (v:string)=>void, // deleteTodo: (v:string)=>void, // loading: boolean, // error: boolean // } export const AppUI:FC = () => { return ( <> <TodoCounter /> <TodoSearch /> { <TodoContext.Consumer> {({ searchedTodos, completeTodo, deleteTodo, loading, error })=>( <TodoList> {loading && ( <> <TodosLoading /> <TodosLoading /> <TodosLoading /> </> )} {error && <TodosError />} {(!loading && searchedTodos.length === 0) && <EmptyTodos />} { searchedTodos.map(todo=>( <TodoItem key={todo.text} text={todo.text} completed={todo.completed} onComplete={()=>{completeTodo(todo.text)}} onDelete={()=>{deleteTodo(todo.text)}} /> )) } </TodoList> )} </TodoContext.Consumer> } <TodoButton /> </> ) }