Poner las cosas en contexto con React

Índice
  1. Hay algunas trampas
  2. Mejores enfoques para los problemas
  3. El nuevo contexto
  4. Contextos separados
  5. Conclusión

Context es actualmente una API experimental para React, ¡pero pronto será un ciudadano de primera clase ! Hay muchas razones por las que es interesante, pero quizás la más importante es que permite que los componentes principales pasen datos implícitamente a sus hijos, sin importar cuán profundo sea el árbol de componentes. En otras palabras, se pueden agregar datos a un componente principal y luego cualquier hijo puede acceder a ellos.

Si bien este suele ser el caso de uso para algo como Redux , es bueno usarlo si no necesita una administración de datos compleja. ¡Piénsalo! Creamos un flujo de datos personalizado, decidiendo qué accesorios se pasan y en qué niveles. Muy genial.

El contexto es excelente en áreas donde hay muchos componentes que dependen de un solo dato, pero que se encuentran en lo más profundo del árbol de componentes. Pasar explícitamente cada accesorio a cada componente individual a menudo puede resultar abrumador y es mucho más fácil simplemente usar el contexto aquí.

Por ejemplo, consideremos cómo normalmente pasaríamos accesorios por el árbol. En este caso, estamos pasando el color redusando accesorios en cada componente para moverlo en el flujo.

class Parent extends React.Component {  render(){    return Child color="red" /;  }}class Child extends React.Component {  render(){    return GrandChild color={this.props.color} /  }}class GrandChild extends React.Component {  render(){    return (      div style={{color: this.props.color}}        Yep, I'm the GrandChild      /div    );  }}

¿Qué pasaría si nunca quisiéramos que el Childcomponente tuviera el accesorio en primer lugar? El contexto nos ahorra tener que revisar el Childcomponente con color y pasarlo directamente de Parenta GrandChild:

class Parent extends React.Component {  // Allow children to use context  getChildContext() {    return {      color: 'red'    };  }    render(){    return Child /;  }}Parent.childContextTypes = {  color: PropTypes.string};class Child extends React.Component {  render() {    // Props is removed and context flows through to GrandChild    return GrandChild /  }}class GrandChild extends React.Component {  render() {    return (      div style={{color: this.context.color}}        Yep, I'm still the GrandChild      /div    );  }}// Expose color to the GrandChildGrandChild.contextTypes = {  color: PropTypes.string};

Si bien es un poco más detallado, la ventaja es exponer colorcualquier parte inferior del árbol de componentes. Bueno, a veces…

Hay algunas trampas

No siempre puedes quedarte con el pastel y comértelo también, y el contexto en su forma actual no es una excepción. Hay algunos problemas subyacentes con los que probablemente entrarás en contacto si terminas utilizando el contexto para todos los casos excepto los más simples.

El contexto es ideal para usarlo en una renderización inicial. ¿Actualizar el contexto sobre la marcha? No tanto. Un problema común con el contexto es que los cambios de contexto no siempre se reflejan en un componente.

Analicemos estos problemas con más detalle.

Gotcha 1: uso de componentes puros

El contexto es difícil cuando se usa PureComponent, ya que de forma predeterminada no realiza ninguna diferencia superficial con el contexto. La diferenciación superficial consiste PureComponenten comprobar si los valores del objeto son estrictamente iguales. Si no es así, entonces (y sólo entonces) se actualizará el componente. Pero como no se comprueba el contexto, bueno… no pasa nada.

Gotcha 2: ¿Debería actualizarse el componente? Tal vez.

El contexto tampoco se actualiza si un componente shouldComponentUpdateregresa false. Si tiene un shouldComponentUpdatemétodo personalizado, también deberá tener en cuenta el contexto. Para habilitar las actualizaciones con contexto, podríamos actualizar cada componente individual con una configuración personalizada shouldComponentUpdatesimilar a esta.

import shallowEqual from 'fbjs/lib/shallowEqual';class ComponentThatNeedsColorContext extends React.PureComponent {  // nextContext will show color as soon as we apply ComponentThatNeedsColorContext.contextTypes  // NOTE: Doing the below will show a console error come react v16.1.1  shouldComponentUpdate(nextProps, nextState, nextContext){    return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState) || !shallowEqual(this.context, nextContext);  }}ComponentThatNeedsColorContext.contextTypes = {  color: PropTypes.string};

Sin embargo, esto no resuelve el problema de un intermediario PureComponententre el padre y el hijo que bloquea las actualizaciones de contexto. Esto significa que cada uno de los métodos PureComponententre el padre y el hijo tendrían que estar contextTypesdefinidos y también tendrían que tener un shouldComponentUpdatemétodo actualizado. Y en este punto, eso es mucho trabajo para muy poco beneficio.

Mejores enfoques para los problemas

Afortunadamente, tenemos algunas formas de solucionar los problemas.

Método 1: utilizar un componente de orden superior

Un componente de orden superior puede leer desde el contexto y pasar los valores necesarios al siguiente componente como accesorio.

import React from 'react';const withColor = (WrappedComponent) = {    class ColorHOC extends React.Component {        render() {            const { color } = this.context;                    return WrappedComponent style={{color: color}} {...this.props} /        }    }             ColorHOC.contextTypes = {        color: React.PropTypes.string      };    return ColorHOC;};export const Button = (props)= button {...props}Button/button// ColoredButton will render with whatever color is currently in context with a style propexport const ColoredButton = withColor( Button );

Enfoque 2: Utilizar accesorios de renderizado

Los Render Props nos permiten usar accesorios para compartir código entre dos componentes.

class App extends React.Component {    getChildContext() {        return {            color: 'red'        }    }    render() {        return Button /    }}App.childContextTypes = {    color: React.PropTypes.string}// Hook 'Color' into 'App' contextclass Color extends React.Component {    render() {        return this.props.render(this.context.color);    }}Color.contextTypes = {    color: React.PropTypes.string}class Button extends React.Component {    render() {        return (            button type="button"                {/* Return colored text within Button */}                Color render={ color = (                    Text color={color} text="Button Text" /                ) } /            /button        )    }}class Text extends React.Component {    render(){        return (            span style={{color: this.props.color}}                {this.props.text}            /span        )    }}Text.propTypes = {    text: React.PropTypes.string,    color: React.PropTypes.string,}

Enfoque 3: Inyección de dependencia

Una tercera forma de solucionar estos problemas es usar la inyección de dependencia para limitar la API de contexto y permitir que los componentes se suscriban según sea necesario.

El nuevo contexto

La nueva forma de usar el contexto, que actualmente está programada para la próxima versión menor de React (16.3) , tiene la ventaja de ser más legible y fácil de escribir sin los “errores” de las versiones anteriores. Ahora tenemos un nuevo método llamado createContext, que define un nuevo contexto y devuelve tanto a Providercomo Consumer.

Establece Providerun contexto al que todos los subcomponentes pueden conectarse. Está conectado a través Consumerdel cual se utiliza un accesorio de renderizado. El primer argumento de esa función render prop es el valueque le hemos dado al Provider. Al actualizar el valor dentro de Provider, todos los consumidores se actualizarán para reflejar el nuevo valor.

Como beneficio adicional al usar el nuevo contexto, ya no tenemos que usar childContextTypes, getChildContext, y contextTypes.

const ColorContext = React.createContext('color');class ColorProvider extends React.Component {    render(){        return (            ColorContext.Provider value={'red'}                { this.props.children }            /ColorContext.Provider        )    }}class Parent extends React.Component {      render(){        // Wrap 'Child' with our color provider        return (            ColorProvider                Child /            /ColorProvider        );    }}class Child extends React.Component {    render(){        return GrandChild /    }}class GrandChild extends React.Component {    render(){        // Consume our context and pass the color into the style attribute        return (            ColorContext.Consumer                {/* 'color' is the value from our Provider */}                {                    color = (                        div style={{color: color}}                            Yep, I'm still the GrandChild                        /div                    )                }            /ColorContext.Consumer        );    }}

Contextos separados

Dado que tenemos un control más granular sobre cómo exponemos el contexto y qué componentes pueden usarlo, podemos envolver componentes individualmente con diferentes contextos, incluso si viven dentro del mismo componente. Podemos ver esto en el siguiente ejemplo, donde al usar LightProviderdos veces, podemos darle a dos componentes un contexto separado.

Conclusión

Context es una API poderosa, pero también es muy fácil de usar incorrectamente. También existen algunas advertencias al usarlo y puede ser muy difícil resolver los problemas cuando los componentes fallan. Si bien los componentes de orden superior y la inyección de dependencias ofrecen alternativas en la mayoría de los casos, el contexto se puede utilizar de manera beneficiosa en porciones aisladas de su código base.

With the next context though, we no longer have to worry about the gotchas we had with the previous version. It removes having to define contextTypes on individual components and opens up the potential for defining new contexts in a reusable manner.

SUSCRÍBETE A NUESTRO BOLETÍN 
No te pierdas de nuestro contenido ni de ninguna de nuestras guías para que puedas avanzar en los juegos que más te gustan.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir

Este sitio web utiliza cookies para mejorar tu experiencia mientras navegas por él. Este sitio web utiliza cookies para mejorar tu experiencia de usuario. Al continuar navegando, aceptas su uso. Mas informacion