El prop drilling es pasar una prop a través de componentes que no la usan, solo para que la pueda usar un componente más abajo en el árbol. Aquí user viaja desde App a través de Page y Sidebar hasta llegar a Avatar, el único componente que lo lee.
Cuatro niveles, y solo al último le importa user. La consola muestra que Page y Sidebar se ejecutan por un valor que nunca muestran. Haz clic en el botón y vuelven a registrar.
Por qué duele
Dos o tres niveles está bien. El prop drilling se vuelve un problema a medida que crece el árbol:
- Los componentes intermedios cargan props que no usan.
PageySidebarahora tienen una propuseren su firma, así que no puedes leerlos sin preguntarte para qué sirve. - Cada cambio toca cada capa. Agrega
user.avatarUrlo un segundo valor comoonLogout, y editas cada componente del camino, no solo los dos extremos. - Mover un componente rompe la cadena. Mueve
Avatara otra rama y tienes que hacer pasaruserpor un camino nuevo. - Es fácil perder una prop. Olvida reenviarla en una capa y el componente profundo recibe
undefined, sin ningún error en la capa que la perdió.
Pruébalo: agrega en App una función onLogout que Avatar deba llamar. Vas a editar los cuatro componentes.
Solución 1: composición con children
Esta es la solución que la gente se salta. En lugar de pasar datos hacia abajo para que un componente profundo pueda renderizar con ellos, deja que el componente dueño de los datos renderice él mismo el componente profundo, y pasa el elemento ya terminado hacia abajo como children (o cualquier otra prop). Las capas intermedias renderizan un hueco y nunca ven user.
Page y Sidebar ya no saben que existe un usuario. App crea Avatar donde vive user y lo entrega hacia abajo como un elemento listo. Los componentes de layout como estos se vuelven reutilizables, y agregar onLogout ahora es un cambio solo en App y en Avatar. Ambos siguen registrando en cada clic, porque App les pasa un elemento nuevo cada vez que renderiza: la composición simplifica el código, no el número de renderizados. La página de children cubre más patrones de huecos.
La composición funciona cuando el componente dueño de los datos también puede decidir cómo se ve la parte profunda. No ayuda cuando el componente profundo está dentro de una librería o de una estructura que el dueño no renderiza.
Solución 2: contexto
Cuando muchos componentes a distintas profundidades necesitan el mismo valor (el tema, el usuario con sesión iniciada, el idioma actual), ponlo en contexto. Cualquier componente bajo el proveedor lo lee con useContext, y nada de lo que hay en medio cambia.
Avatar y Greeting leen el usuario desde dos profundidades distintas, y Page y Sidebar no reciben props. Agrega un tercer lector en cualquier lugar bajo Page y funciona sin tocar el resto. El costo: el flujo de datos ya no se ve en las props, y cada lector renderiza cuando el valor cambia. La página de useContext cubre los proveedores, los valores por defecto y esos renderizados.
Solución 3: una librería de estado
Para un estado de app grande que leen muchos componentes y que cambia a menudo (un carrito, un editor de documentos, datos en vivo), una librería de estado como Redux Toolkit, Zustand o Jotai permite que cada componente se suscriba solo a la parte que usa. Eso evita tanto el drilling como el costo de "cada consumidor renderiza" de un contexto grande. También es otra dependencia y otro conjunto de conceptos, así que recurre a ella cuando las dos primeras soluciones dejen de bastar, no para arreglar una prop que viaja tres niveles.
// Zustand, for comparison (not available in the editor here)
const useCart = create((set) => ({
items: [],
add: (item) => set((s) => ({ items: [...s.items, item] })),
}));
function CartCount() {
const count = useCart((s) => s.items.length); // renders only when the count changes
return <span>{count}</span>;
}
Cuándo el prop drilling está bien
Pasar props es como fluyen los datos en React, y es el patrón más fácil de leer: puedes seguir un valor de arriba abajo mirando el JSX. Una prop que pasa por uno o dos componentes que usan parte de ella, o por una cadena corta de componentes estrechamente relacionados, no es un problema que haya que resolver. Eleva el estado al padre común más cercano, pásalo hacia abajo, y cambia de enfoque solo cuando las capas intermedias empiecen a cargar props que no les sirven para nada.
Preguntas frecuentes
¿Qué es el prop drilling en React?
Pasar una prop hacia abajo a través de varias capas de componentes que no la usan, solo para que un componente en lo profundo del árbol pueda leerla. Cada componente intermedio acepta la prop y la reenvía.
¿El prop drilling es malo?
No por sí mismo. Pasar un valor dos o tres niveles hacia abajo es explícito y fácil de seguir. Se convierte en un problema cuando muchas capas cargan props que nunca usan, así que cada cambio de nombre o campo nuevo toca archivos que no tienen nada que ver.
¿Cómo evito el prop drilling?
Prueba primero la composición: deja que el padre construya el componente profundo y lo pase hacia abajo como children u otra prop, para que las capas intermedias nunca vean los datos. Si muchos componentes a distintas profundidades necesitan el valor, usa contexto. Para un estado grande que cambia a menudo, considera una librería de estado.
¿El contexto es la única solución para el prop drilling?
No, y a menudo no es la mejor. La composición con children elimina el drilling sin agregar un contexto, y mantiene visible el flujo de datos en el código.