Contextos como Proveedores
Compartir valores contextuales entre componentes anidados al proveedor
Es momento de atacar otro de los hooks más importantes de React, el hook de contextos — useContext, que nos permitirá compartir un valor, entre múltiples componentes anidados, sin importar el nivel de anidación, siempre y cuándo se encuentren dentro del proveedor. Esto suele ser útil, para compartir valores y estados entre múltiples componentes, sin tener que enviar los valores como entradas y salidas mediante propiedades.
El uso de contextos debe ser cuidadoso y preciso, ya que cada que cambie un valor en el contexto, este mandará a renderizar los componentes que hagan uso de este valor, por lo que es muy común bajar el rendimiento de nuestras aplicaciones. Sin embargo, cuándo los valores no cambian tanto o su dependencia entre múltiples componentes es alta, conviene su uso, para de este modo, acceder a valores compartidos ente todos.
Algo común es compartir estados y ajustadores, reductores y despachadores, valores y funciones personalizadas de ajuste e interfaces de programación optimizadas mediante hooks personalizados.
La instancia del contexto
Un contexto se crea mediante React.context
, este devolverá una instancia al contexto, que podrá ser utilizada por el hook useContext, de modo que cualquier componente pueda acceder al valor provisto de la instancia. La instancia relaciona el componente Provider
y el componente Consumer
, los cuáles permitirán proveer el contexto y consumirlo directamente.
Sintaxis
const MyContext = React.createContext(defaultValue);
Donde
MyContext
es la instancia del contexto ydefaultValue
el valor por defecto, en caso de que el proveedor no especifique uno.
Proveedor del contexto
El proveedor del contexto, es un componente, que establece el valor a compartir en el contexto, y anida a todos los componentes que tengan alcance a este valor. El concepto es similar, a que el proveedor crea una habitación, dónde se está compartiendo un valor, y todos los componentes dentro de la habitación, tendrán acceso al valor compartido. Cuándo el valor del proveedor cambia, todos dentro de la habitación, serán notificados, para que se vuelvan a pintar.
Sintaxis
<MyContext.Provider value={valor compartido}>
...componentes con alcance al valor del contexto, habitación
</MyContext.Provider>
Donde
MyContext.Provider
es el componente proveedor, de la instanciaMyContext
. Este componente anida a todos los otros componentes, formando una habitación, dónde, cada componente dentro de la habitación, tendrá acceso al valor del contexto medianteuseContext
y se volverá a pintar, cuándo el contexto cambie.
Consumidor del contexto
Un consumidor directo del contexto, es un componente que anida una función de consumo del valor del contexto, y devuelve la vista adecuada, según ese valor. Es útil cuándo no se quiere formalizar algún componente, y ahorrar el consumo del hook useContext, no es recomendable.
Sintaxis
<MyContext.Provider value={myValue}>
...
<MyContext.Consumer>
{ myValue => { ...(devuelve interfaz) } }
</MyContext.Consumer>
...
</MyContext.Provider>
Donde
MyContext.Consumer
es el componente especial de consumo, que recibe la función de consumo, cuyo parámetro es el valor compartidomyValue
del contexto, y devuelve la interfaz adecuada para pintarse onull
.
Consumir el contexto dentro de un componente
Finalmente, los componentes, que se encuentren anidados en un proveedor, en cualquier nivel de anidación, tendrán acceso al valor compartido en el contexto, mediante el hook useContext.
Sintaxis
function MyComponent() {
const value = useContext(MyContext);
}
Donde
value
es el valor contextual yMyContext
es la instancia creada del contexto.
Ejemplo
En el siguiente ejemplo, se observa la creación de un contexto sencillo, que retiene el número de clics.
- Observa que se crea una instancia de un contexto, con el valor por defecto
{ clicks, setClicks }
, donde se observa, quesetClicks
es una función aún no implementada - Observa que se define el componente
ClickButton
, que accede al valor del contexto, medianteuseContext(ClicksContext)
. - Observa que el valor del contexto devuelto, llamado
context
, contienecontext.clicks
ycontext.setClicks
. Los cuáles son utilizados, para acceder al valor de los clics y ajustarlos. - Observa que en el componente
App
, se define un estado[clicks, setClicks]
que retendrá los clics. - Observa la creación de un proveedor mediante
ClickContext.Provider
, el cuál anida un consumidorClicksContext.Consumer
y un componente llamadoClickButton
. - Observa que el valor asignado a
<ClickContext.Provider value={...}>
, es un objeto con las claves{ clicks, setClicks }
, y se asocian a las variables del esto[clicks, setClicks]
. Es decir, el valor del proveedor, es el estadoclicks-setClicks
. - Observa que el consumidor
<ClicksContext.Consumer>
, anida una función que recibe como parámetro el valor del contextocontext
. De él se accede acontext.clicks
para devolver un<span>
como interfaz. - Observa que se monta el componente
<ClickButton>
, que muestra el botón, que al ser pulsado, incrementa el número de clics, mediantecontext.setContext(context.clicks + 1)
. Este manda a actualizar el valor del estadoclicks-setClicks
, y finalmente, el proveedor actualiza el valor del contexto.
Proyecto — Login Lock Context
En este proyecto, vemos el uso de los contexto, para crear un componente que bloquee a los componentes hijos anidados. De modo, que hasta que no se inicie sesión, muestre un formulario de bloqueo.
Revisa cuidadosamente la estructura del proyecto, la organización del código, el formulario independiente al contexto, la salida del formulario props.onSignIn
y la forma segura de consumirlo. Revisa también el hook personalizado useLoginLock
. La interfaz devuelta, la creación de un proveedor formalizado, el consumo del formulario. Y finalmente, revisa en App.js el uso del proveedor y sus resultados.
Conclusiones
Hemos aprendido a utilizar el hook de contextos, el cuál nos permite compartir un valor, mediante un proveedor, que informará los cambios sobre el valor compartido, a todos los componentes anidados. Esto será útil, cuando queramos compartir valores que no cambien mucho, o que sea utilizado en demasiados componentes.
Los contextos, evitan que estemos propagando los valores de un componente a otro, y crear más bien una especie de habitación, dónde todos los elementos anidados en la habitación, tendrán acceso al valor del contexto.
Debemos tener cuidado en su uso, y limitarlo a casos especiales, dónde su conveniencia sea bastante útil, por ejemplo, cuándo los valores compartidos no cambien mucho, como en la paleta de colores de la aplicación, inicios de sesión, carritos de compra, y demás bolsas de valores compartidos, que cambian, conforme el usuario interactúa con la aplicación, y no lo hacen automáticamente, como en contadores y así. Un segundo caso opcional dónde podremos usar contextos, es cuándo aunque los valores cambien mucho, será necesario compartirlos a través de gran cantidad de componentes, por ejemplo, una transacción en tiempo real, un chat, una tabla en tiempo real, etc.
Documentación
Contextos
Uso avanzado de contextos
Stackblitz oficial de Dragon Nomada
Artículos de React por Dragon Nomada y colaboradores