En React, el contexto es una forma de pasar datos entre árboles de componentes sin agregar accesorios manualmente para cada capa de componentes. El contexto proporciona una manera de compartir valores específicos entre componentes sin tener que pasarlos explícitamente a través del componente capa por capa. en el árbol.
El entorno operativo de este tutorial: sistema Windows 10, versión de reacción 17.0.1, computadora Dell G3.
El contexto proporciona una forma de pasar datos entre árboles de componentes sin agregar accesorios manualmente a cada capa de componentes. En una aplicación React típica, los datos se pasan de arriba a abajo (de padre a hijo) a través de accesorios, pero este enfoque es extremadamente engorroso para ciertos tipos de propiedades (por ejemplo, preferencias locales, tema de UI), estas propiedades son requeridas por muchos componentes en la aplicación. El contexto proporciona una manera de compartir dichos valores entre componentes sin tener que pasar accesorios explícitamente a través de cada nivel del árbol de componentes.
El contexto está diseñado para compartir datos que son "globales" en un árbol de componentes, como el usuario, el tema o el idioma preferido actualmente autenticado. Por ejemplo, en el siguiente código, ajustamos manualmente el estilo de un componente de botón mediante un atributo "tema".
class App extends React.Component { render() { return <Toolbar theme="dark" /> }}function Toolbar(props) { // El componente de la barra de herramientas acepta una propiedad "tema" adicional, que luego se pasa al ThemedButton. componente. // Sería problemático si cada botón de la aplicación necesitara conocer el valor del tema, // porque este valor tendría que transmitirse a todos los componentes. return ( <p> <ThemedButton theme={props.theme} /> </p> );}clase ThemedButton extiende React.Component { render() { return <Button theme={this.props.theme} />; }// Pasar accesorios: Aplicación -> Barra de herramientas -> ThemedButton// Si el anidamiento es muy profundo, entonces los accesorios deben pasarse capa por capa, incluso si los accesorios no son necesarios en el medio, parece muy engorroso.Usando el contexto, podemos evitar pasar accesorios a través de elementos intermedios.
// El contexto nos permite pasar valores a lo más profundo del árbol de componentes sin tener que pasarlos explícitamente a través de cada componente. // Crea un contexto para el tema actual ("ligero" es el valor predeterminado). const ThemeContext = React.createContext('light');class App extends React.Component { render() { // Utilice un proveedor para pasar el tema actual al siguiente árbol de componentes. // No importa cuán profundo sea, cualquier componente puede leer este valor. // En este ejemplo, pasamos "oscuro" como valor actual. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> }}// El componente intermedio ya no necesita especificar el tema que se transmitirá. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton extends React.Component { // Especifique contextType para leer el contexto del tema actual. // React encontrará el proveedor de temas más cercano y utilizará su valor. // En este ejemplo, el valor del tema actual es "oscuro". static contextType = ThemeContext; render() { return <Button theme={this.context} /> }}// También puedes usar ThemedButto.contextType = ThemeContext;Crea un objeto de contexto. Cuando React representa un componente suscrito a este objeto Context, el componente leerá el valor de contexto actual del Proveedor coincidente más cercano a él en el árbol de componentes.
Solo cuando no haya ningún proveedor coincidente en el árbol donde se encuentra el componente, su parámetro defaultValue entrará en vigor. Esto ayuda a probar componentes sin utilizar un proveedor para empaquetar el componente. Nota: Cuando se pasa indefinido al valor del proveedor, el valor predeterminado del componente consumidor no tendrá efecto.
Cada objeto Context devuelve un componente Provider React, que permite que los componentes consumidores se suscriban a los cambios de contexto.
El proveedor recibe un atributo de valor y lo pasa al componente consumidor. Un Proveedor puede tener una relación correspondiente con múltiples componentes de consumidores. También se pueden utilizar varios proveedores anidados y la capa interna sobrescribirá los datos de la capa externa.
Cuando el valor del Proveedor cambie, todos los componentes que lo consumen se volverán a representar. Ni el proveedor ni sus componentes consumidores internos están sujetos a la función shouldComponentUpdate, por lo que el componente consumidor se puede actualizar incluso si su componente ancestro deja de actualizarse.
El atributo contextType montado en la clase se reasignará a un objeto Context creado por React.createContext(). Esto le permite usar this.context para consumir el valor en el contexto más reciente. Puede acceder a él en cualquier ciclo de vida, incluida la función de renderizado.
import MyContext from './MyContext';class MyClass extends React.Component { componenteDidMount() { let value = this.context /* Después de montar el componente, use el valor del componente MyContext para realizar algunas operaciones de efectos secundarios*; / } componenteDidUpdate() { dejar valor = this.context; /* ... */ } componenteWillUnmount() { dejar valor = this.context /* ... */ } render() { dejar valor = this.context; ; /* Representar según el valor del componente MyContext*/ } // O usar static contextType = MyContext;}MyClass.contextType = MyContext;Aquí, los componentes de React también pueden suscribirse a cambios de contexto. Esto le permite suscribirse al contexto en componentes funcionales.
Esto requiere funcionar como un niño. Esta función recibe el valor del contexto actual y devuelve un nodo React. El valor pasado a la función es equivalente al valor proporcionado por el proveedor más cercano a este contexto en el árbol de componentes. Si no hay un proveedor correspondiente, el parámetro de valor es equivalente al valor predeterminado pasado a createContext().
El objeto de contexto acepta una propiedad denominada displayName, que es de tipo cadena. React DevTools usa esta cadena para determinar qué contexto mostrar.
El siguiente componente aparecerá como MyDisplayName en DevTools
const MyContext = React.createContext(/* algún valor */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" en DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" en Herramientas de desarrolloPara el ejemplo de tema anterior, se puede lograr un uso más complejo utilizando valores dinámicos.
tema-contexto.js
exportar temas constantes = { claro: { primer plano: '#000000', fondo: '#eeeeee', }, oscuro: { primer plano: '#ffffff', fondo: '#222222', },}; exportar const ThemeContext = React .createContext(themes.dark); // Este es el valor predeterminadobotón-temático.js
importar { ThemeContext } desde './theme-context'; class ThemedButton extends React.Component { render() { let props = this.props // Obtener el valor predeterminado en ThemeContext let theme = this.context return ( < botón; {...props} estilo={{fondoColor: tema.fondo}} /> } // tipo de contexto estático = ThemeContext;}ThemedButton.contextType = ThemeContext;exportar botón temático predeterminado;aplicación.js
import { ThemeContext, temas } from './theme-context';import ThemedButton from './themed-button';// Una función de componente intermedio que usa ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > Cambiar tema </ThemedButton>);} clase Aplicación extiende React.Component { constructor(props) { super(props); this.state = { tema: temas.light, }; .setState(state => ({ tema: state.theme === temas.dark ? temas.light : temas.dark, })} } render() { // Usado por el componente del botón ThemedButton dentro de ThemeProvider El tema; valor en estado, // y los componentes externos usan el valor de tema predeterminado return ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> ); }}ReactDOM.render(<App />, document.root);// Los componentes empaquetados por ThemeContext.Provider pueden consumir ThemeContext El valor // en la barra de herramientas y ThemedButton puede usar this.context para obtener el valor // Tenga en cuenta que el método de actualización del estado es transmitirlo a través de accesorios, y la actualización es activada por componentes descendientes. El método a través del contexto se analizará a continuación.En el ejemplo anterior, pasamos una función de actualización a través de accesorios para cambiar el valor de los temas en la aplicación. Sabemos que es necesario actualizar el contexto de un componente que está profundamente anidado en el árbol de componentes. En este escenario, puede pasar una función a través del contexto para que el componente consumidor actualice el contexto.
tema-contexto.js
// ¡Asegúrese de que la estructura de datos del valor predeterminado pasada a createContext coincida con los componentes que llaman (consumidores)! export const ThemeContext = React.createContext({ tema: temas.dark, toggleTheme: () => {}, // Definir el método para actualizar el tema, transmitirlo});botón-alternador-de-tema.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // El botón Theme Toggler no solo obtiene el valor del tema, sino que también obtiene una función toggleTheme del contexto (parte de app.js a continuación) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Alternar tema </button> )} </ThemeContext.Consumer> ); }exportar ThemeTogglerButton predeterminado;aplicación.js
importar { ThemeContext, temas } desde './theme-context'; importar ThemeTogglerButton desde './theme-toggler-button'; la aplicación de clase extiende React.Component { constructor(props) { super(props); ) => { this.setState(state => ({ tema: state.theme === temas.dark ? temas.light : temas.dark, }) }; Se pasará al proveedor de contexto. this.state = { theme: theme.light, toggleTheme: this.toggleTheme, // Definir la función de actualización y pasarla a través del contexto } } render() { // Todo el estado se pasa al proveedor return ( < ThemeContext.Valor del proveedor={this.state}> <Contenido /> </ThemeContext.Provider> ); }}función Contenido() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <Aplicación />, documento.root);Para garantizar que el contexto se vuelva a representar rápidamente, React necesita hacer que el contexto de cada componente del consumidor sea un nodo separado en el árbol de componentes.
// Contexto del tema, el tema predeterminado es el valor "ligero" const ThemeContext = React.createContext('light'); // Contexto de inicio de sesión del usuario const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const { signedInUser, theme } = this.props; // Componente de la aplicación que proporciona el valor de contexto inicial return ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > < Layout /> </UserContext.Provider> </ThemeContext.Provider> ); }}function Layout() { return ( <p> <Sidebar /> <Content /> </p> );}// Un componente puede consumir múltiples funciones de contexto Contenido() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext . Consumidor> )} </ThemeContext.Consumer> );}Si dos o más valores de contexto se usan juntos con frecuencia, es posible que desee considerar la posibilidad de crear su propio componente de representación para proporcionar estos valores.
Debido a que el contexto utiliza una identidad de referencia para decidir cuándo renderizar, puede haber algunos errores que pueden desencadenar una representación inesperada en el componente consumidor cuando el componente principal del proveedor se vuelve a renderizar. Por ejemplo, cada vez que se vuelve a representar el Proveedor, el siguiente código volverá a representar todos los siguientes componentes del consumidor, porque el atributo de valor siempre se asigna a un nuevo objeto.
La aplicación de clase extiende React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> }}Para evitar esta situación, el estado del valor se promueve al estado del nodo principal.
class App extends React.Component { constructor(props) { super(props); // Después de varias renderizaciones, el estado se conservará. Cuando el valor no cambie, los siguientes componentes consumidores no volverán a renderizar this.state = { valor: { algo: 'algo'}, }; render() { return ( <Valor del proveedor={this.state.value}> <Barra de herramientas /> </Provider> }}