v19.2Latest

Pasar datos en profundidad con Context

Normalmente, pasarás información de un componente padre a un componente hijo mediante props. Pero pasar props puede volverse verboso e inconveniente si tienes que pasarlos a través de muchos componentes intermedios, o si muchos componentes de tu aplicación necesitan la misma información.Contextpermite al componente padre poner cierta información disponible para cualquier componente en el árbol debajo de él—sin importar cuán profundo—sin pasarla explícitamente a través de props.

Aprenderás
  • Qué es el “prop drilling”
  • Cómo reemplazar el paso repetitivo de props con context
  • Casos de uso comunes para context
  • Alternativas comunes a context

El problema de pasar props

Pasar propses una excelente manera de canalizar datos explícitamente a través de tu árbol de UI hacia los componentes que los usan.

Pero pasar props puede volverse verboso e inconveniente cuando necesitas pasar alguna prop profundamente a través del árbol, o si muchos componentes necesitan la misma prop. El ancestro común más cercano podría estar muy alejado de los componentes que necesitan datos, yelevar el estadotan alto puede llevar a una situación llamada “prop drilling”.

Elevar el estado

Diagrama con un árbol de tres componentes. El padre contiene una burbuja que representa un valor resaltado en púrpura. El valor fluye hacia abajo a cada uno de los dos hijos, ambos resaltados en púrpura.Diagrama con un árbol de tres componentes. El padre contiene una burbuja que representa un valor resaltado en púrpura. El valor fluye hacia abajo a cada uno de los dos hijos, ambos resaltados en púrpura.

Prop drilling

Diagrama con un árbol de diez nodos, cada nodo con dos hijos o menos. El nodo raíz contiene una burbuja que representa un valor resaltado en púrpura. El valor fluye hacia abajo a través de los dos hijos, cada uno de los cuales pasa el valor pero no lo contiene. El hijo izquierdo pasa el valor hacia abajo a dos hijos que están ambos resaltados en púrpura. El hijo derecho de la raíz pasa el valor a través de uno de sus dos hijos - el derecho, que está resaltado en púrpura. Ese hijo pasó el valor a través de su único hijo, que lo pasa hacia abajo a sus dos hijos, que están resaltados en púrpura.Diagrama con un árbol de diez nodos, cada nodo con dos hijos o menos. El nodo raíz contiene una burbuja que representa un valor resaltado en púrpura. El valor fluye hacia abajo a través de los dos hijos, cada uno de los cuales pasa el valor pero no lo contiene. El hijo izquierdo pasa el valor hacia abajo a dos hijos que están ambos resaltados en púrpura. El hijo derecho de la raíz pasa el valor a través de uno de sus dos hijos - el derecho, que está resaltado en púrpura. Ese hijo pasó el valor a través de su único hijo, que lo pasa hacia abajo a sus dos hijos, que están resaltados en púrpura.

¿No sería genial si hubiera una manera de “teletransportar” datos a los componentes del árbol que los necesitan sin pasar props? ¡Con la función de context de React, la hay!

Context: una alternativa a pasar props

Context permite que un componente padre proporcione datos a todo el árbol debajo de él. Hay muchos usos para context. Aquí hay un ejemplo. Considera este componenteHeadingque acepta unlevelpara su tamaño:

Digamos que quieres que múltiples encabezados dentro de la mismaSectiontengan siempre el mismo tamaño:

Actualmente, pasas la proplevela cada<Heading>por separado:

Sería bueno si pudieras pasar la proplevelal componente<Section>en su lugar y eliminarla del<Heading>. De esta manera podrías asegurar que todos los encabezados en la misma sección tengan el mismo tamaño:

Pero, ¿cómo puede el componente<Heading>saber el nivel de su<Section>más cercano?Eso requeriría alguna forma para que un hijo "pregunte" por datos desde algún lugar superior en el árbol.

No puedes hacerlo solo con props. Aquí es donde entra en juego el contexto. Lo harás en tres pasos:

  1. Crearun contexto. (Puedes llamarloLevelContext, ya que es para el nivel de encabezado).
  2. Usarese contexto desde el componente que necesita los datos. (HeadingusaráLevelContext).
  3. Proporcionarese contexto desde el componente que especifica los datos. (SectionproporcionaráLevelContext).

El contexto permite que un padre—¡incluso uno lejano!—proporcione algunos datos a todo el árbol dentro de él.

Usando contexto en hijos cercanos

Diagrama con un árbol de tres componentes. El padre contiene una burbuja que representa un valor resaltado en naranja que se proyecta hacia abajo a los dos hijos, cada uno resaltado en naranja.Diagrama con un árbol de tres componentes. El padre contiene una burbuja que representa un valor resaltado en naranja que se proyecta hacia abajo a los dos hijos, cada uno resaltado en naranja.

Usando contexto en hijos lejanos

Diagrama con un árbol de diez nodos, cada nodo con dos hijos o menos. El nodo padre raíz contiene una burbuja que representa un valor resaltado en naranja. El valor se proyecta directamente hacia cuatro hojas y un componente intermedio en el árbol, todos resaltados en naranja. Ninguno de los otros componentes intermedios está resaltado.Diagrama con un árbol de diez nodos, cada nodo con dos hijos o menos. El nodo padre raíz contiene una burbuja que representa un valor resaltado en naranja. El valor se proyecta directamente hacia cuatro hojas y un componente intermedio en el árbol, todos resaltados en naranja. Ninguno de los otros componentes intermedios está resaltado.

Paso 1: Crear el contexto

Primero, necesitas crear el contexto. Necesitarásexportarlo desde un archivopara que tus componentes puedan usarlo:

El único argumento paracreateContextes el valorpredeterminado. Aquí,1se refiere al nivel de encabezado más grande, pero podrías pasar cualquier tipo de valor (incluso un objeto). Verás la importancia del valor predeterminado en el siguiente paso.

Paso 2: Usar el contexto

Importa el HookuseContextde React y tu contexto:

Actualmente, el componenteHeading lee levelde las props:

En su lugar, elimina la proplevely lee el valor del contexto que acabas de importar,LevelContext:

useContextes un Hook. Al igual queuseState y useReducer, solo puedes llamar a un Hook directamente dentro de un componente de React (no dentro de bucles o condiciones).useContextle dice a React que el componenteHeadingquiere leer elLevelContext.

Ahora que el componenteHeadingno tiene una proplevel, ya no necesitas pasar la prop level aHeadingen tu JSX así:

Actualiza el JSX para que sea laSectionla que lo reciba en su lugar:

Como recordatorio, este es el marcado que estabas intentando hacer funcionar:

Observa que este ejemplo aún no funciona del todo. Todos los encabezados tienen el mismo tamaño porqueaunque estásusandoel contexto, aún no lo hasproporcionado.¡React no sabe de dónde obtenerlo!

Si no proporcionas el contexto, React usará el valor predeterminado que especificaste en el paso anterior. En este ejemplo, especificaste1como argumento paracreateContext, por lo queuseContext(LevelContext)devuelve1, estableciendo todos esos encabezados como<h1>. Resolvamos este problema haciendo que cadaSectionproporcione su propio contexto.

Paso 3: Proporcionar el contexto

El componenteSectionactualmente renderiza sus hijos:

Envuelve los hijos con un proveedor de contextopara proporcionarles elLevelContext:

Esto le dice a React: "si cualquier componente dentro de este<Section>solicitaLevelContext, dales estelevel". El componente usará el valor del<LevelContext>más cercano en el árbol de UI por encima de él.

Es el mismo resultado que el código original, pero no necesitas pasar la proplevela cada componenteHeading. En su lugar, "descubre" su nivel de encabezado preguntando a laSectionmás cercana por encima:

  1. Pasas una proplevel al <Section>.
  2. Sectionenvuelve a sus hijos en<LevelContext value={level}>.
  3. Headingsolicita el valor más cercano deLevelContextque esté por encima conuseContext(LevelContext).

Usar y proporcionar contexto desde el mismo componente

Actualmente, todavía tienes que especificar manualmente ellevelde cada sección:

Dado que el contexto te permite leer información de un componente superior, cadaSectionpodría leer elleveldelSectionque está por encima, y pasarlevel + 1hacia abajo automáticamente. Así es como podrías hacerlo:

Con este cambio, ya no necesitas pasar la proplevel ni al <Section>ni al<Heading>:

Ahora tantoHeadingcomoSectionleen elLevelContextpara averiguar qué tan "profundos" están. Y elSectionenvuelve a sus hijos en elLevelContextpara especificar que cualquier cosa dentro de él está en un nivel "más profundo".

Nota

Este ejemplo utiliza niveles de encabezado porque muestran visualmente cómo los componentes anidados pueden anular el contexto. Pero el contexto es útil para muchos otros casos de uso. Puedes pasar cualquier información que necesite todo el subárbol: el tema de color actual, el usuario que ha iniciado sesión actualmente, etc.

El contexto pasa a través de componentes intermedios

Puedes insertar tantos componentes como quieras entre el componente que proporciona el contexto y el que lo usa. Esto incluye tanto componentes integrados como<div>como componentes que puedas construir tú mismo.

En este ejemplo, el mismo componentePost(con un borde discontinuo) se renderiza en dos niveles de anidamiento diferentes. Observa que el<Heading>dentro de él obtiene su nivel automáticamente del<Section>más cercano:

No hiciste nada especial para que esto funcione. UnaSectionespecifica el contexto para el árbol dentro de ella, por lo que puedes insertar un<Heading>en cualquier lugar, y tendrá el tamaño correcto. ¡Pruébalo en el sandbox de arriba!

El contexto te permite escribir componentes que "se adaptan a su entorno" y se muestran de manera diferente dependiendo dedónde(o, en otras palabras,en qué contexto) se están renderizando.

El funcionamiento del contexto podría recordarte a laherencia de propiedades CSS.En CSS, puedes especificarcolor: bluepara un<div>, y cualquier nodo DOM dentro de él, sin importar cuán profundo esté, heredará ese color a menos que algún otro nodo DOM en el medio lo anule concolor: green. De manera similar, en React, la única forma de anular algún contexto proveniente de arriba es envolver a los hijos en un proveedor de contexto con un valor diferente.

En CSS, propiedades diferentes comocolor y background-colorno se anulan entre sí. Puedes establecer el<div>’scoloren rojo sin afectar elbackground-color. De manera similar,los diferentes contextos de React no se anulan entre sí.Cada contexto que creas concreateContext()está completamente separado de los demás, y vincula componentes que usan y proveenese contexto particular. Un componente puede usar o proveer muchos contextos diferentes sin problema.

Antes de usar el contexto

El contexto es muy tentador de usar. Sin embargo, esto también significa que es demasiado fácil abusar de él.El hecho de que necesites pasar algunas props varios niveles hacia abajo no significa que debas poner esa información en el contexto.

Aquí hay algunas alternativas que deberías considerar antes de usar el contexto:

  1. Comienza porpasar props.Si tus componentes no son triviales, no es inusual pasar una docena de props a través de una docena de componentes. Puede parecer tedioso, pero deja muy claro qué componentes usan qué datos. La persona que mantiene tu código estará agradecida de que hayas hecho explícito el flujo de datos con props.
  2. Extrae componentes ypasa JSX como hijosa ellos.Si pasas algunos datos a través de muchas capas de componentes intermedios que no usan esos datos (y solo los pasan más abajo), esto a menudo significa que olvidaste extraer algunos componentes en el camino. Por ejemplo, tal vez pasas props de datos comopostsa componentes visuales que no los usan directamente, como<Layout posts={posts} />. En su lugar, haz queLayouttomechildrencomo prop, y renderiza<Layout><Posts posts={posts} /></Layout>. Esto reduce el número de capas entre el componente que especifica los datos y el que los necesita.

Si ninguno de estos enfoques funciona bien para ti, considera el contexto.

Casos de uso para el contexto

  • Temas:Si tu aplicación permite al usuario cambiar su apariencia (por ejemplo, modo oscuro), puedes colocar un proveedor de contexto en la parte superior de tu aplicación y usar ese contexto en los componentes que necesiten ajustar su apariencia visual.
  • Cuenta actual:Muchos componentes pueden necesitar conocer al usuario que ha iniciado sesión actualmente. Ponerlo en contexto hace que sea conveniente leerlo en cualquier parte del árbol. Algunas aplicaciones también te permiten operar con múltiples cuentas al mismo tiempo (por ejemplo, para dejar un comentario como un usuario diferente). En esos casos, puede ser conveniente envolver una parte de la interfaz de usuario en un proveedor anidado con un valor de cuenta actual diferente.
  • Enrutamiento:La mayoría de las soluciones de enrutamiento usan contexto internamente para mantener la ruta actual. Así es como cada enlace "sabe" si está activo o no. Si construyes tu propio enrutador, es posible que también quieras hacerlo.
  • Gestión del estado:A medida que tu aplicación crece, puedes terminar con mucho estado cerca de la parte superior de tu aplicación. Muchos componentes distantes debajo pueden querer cambiarlo. Es comúnusar un reductor junto con el contextopara gestionar un estado complejo y pasarlo a componentes distantes sin demasiadas complicaciones.

El contexto no se limita a valores estáticos. Si pasas un valor diferente en el siguiente renderizado, ¡React actualizará todos los componentes que lo lean debajo! Por eso el contexto a menudo se usa en combinación con el estado.

En general, si alguna información es necesaria para componentes distantes en diferentes partes del árbol, es una buena indicación de que el contexto te ayudará.

Recapitulación

  • El contexto permite que un componente proporcione información a todo el árbol debajo de él.
  • Para pasar contexto:
    1. Créalo y expórtalo conexport const MyContext = createContext(defaultValue).
    2. Pásalo al HookuseContext(MyContext)para leerlo en cualquier componente hijo, sin importar cuán profundo esté.
    3. Envuelve a los hijos en<MyContext value={...}>para proporcionarlo desde un padre.
  • El contexto pasa a través de cualquier componente intermedio.
  • El contexto te permite escribir componentes que "se adaptan a su entorno".
  • Antes de usar contexto, intenta pasar props o pasar JSX comochildren.

Prueba algunos desafíos

Challenge 1 of 1:Reemplaza el prop drilling con contexto #

En este ejemplo, activar la casilla de verificación cambia la prop imageSize que se pasa a cada <PlaceImage>. El estado de la casilla se mantiene en el componente de nivel superior App, pero cada <PlaceImage> necesita ser consciente de ello.

Actualmente, App pasa imageSize a List, que lo pasa a cada Place, que lo pasa al PlaceImage. Elimina la prop imageSize, y en su lugar pásala desde el componente App directamente a PlaceImage.

Puedes declarar el contexto en Context.js.


Pasar Datos Profundamente con Contexto | React Learn - Reflow Hub