Dans React, le contexte est un moyen de transmettre des données entre les arborescences de composants sans ajouter manuellement d'accessoires pour chaque couche de composants ; le contexte fournit un moyen de partager les valeurs spécifiées entre les composants sans avoir à les transmettre explicitement couche par couche. dans l'arbre.
L'environnement d'exploitation de ce tutoriel : système Windows 10, version React 17.0.1, ordinateur Dell G3.
Le contexte fournit un moyen de transmettre des données entre les arborescences de composants sans ajouter manuellement d'accessoires à chaque couche de composants. Dans une application React typique, les données sont transmises de haut en bas (du parent à l'enfant) via des accessoires, mais cette approche est extrêmement lourde pour certains types de propriétés (par exemple, les préférences locales, le thème de l'interface utilisateur), ces propriétés sont requises par de nombreux composants dans la demande. Le contexte fournit un moyen de partager ces valeurs entre les composants sans avoir à transmettre explicitement des accessoires à chaque niveau de l'arborescence des composants.
Le contexte est conçu pour partager des données « globales » avec une arborescence de composants, telles que l'utilisateur actuellement authentifié, le thème ou la langue préférée. Par exemple, dans le code suivant, nous ajustons manuellement le style d'un composant bouton via un attribut "theme"
class App extends React.Component { render() { return <Toolbar theme="dark" />; }}function Toolbar(props) { // Le composant Toolbar accepte une propriété "theme" supplémentaire, qui est ensuite transmise à ThemedButton composant. // Ce serait gênant si chaque bouton de l'application devait connaître la valeur du thème, // car cette valeur devrait être transmise à tous les composants. return ( <p> <ThemedButton theme={props.theme} /> </p> );}class ThemedButton extends React.Component { render() { return <Button theme={this.props.theme} />; }// Passer les accessoires : App -> Toolbar -> ThemedButton// Si l'imbrication est très profonde, alors les accessoires doivent être passés couche par couche, même si les accessoires ne sont pas nécessaires au milieu, cela semble très fastidieux.En utilisant le contexte, nous pouvons éviter de passer des accessoires à travers des éléments intermédiaires
// Le contexte nous permet de transmettre des valeurs en profondeur dans l'arborescence des composants sans avoir à les transmettre explicitement à travers chaque composant. // Crée un contexte pour le thème actuel ("light" est la valeur par défaut). const ThemeContext = React.createContext('light');class App extends React.Component { render() { // Utilisez un fournisseur pour transmettre le thème actuel à l'arborescence de composants suivante. // Quelle que soit la profondeur, n'importe quel composant peut lire cette valeur. // Dans cet exemple, nous passons "dark" comme valeur actuelle. return ( <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); }}// Le composant intermédiaire n'a plus besoin de spécifier le thème à transmettre. function Toolbar() { return ( <p> <ThemedButton /> </p> );}class ThemedButton extends React.Component { // Spécifiez contextType pour lire le contexte du thème actuel. // React trouvera le fournisseur de thème le plus proche et utilisera sa valeur. // Dans cet exemple, la valeur actuelle du thème est "dark". static contextType = ThemeContext; render() { return <Button theme={this.context} />; }}// Vous pouvez également utiliser ThemedButto.contextType = ThemeContext;Créez un objet Contexte. Lorsque React restitue un composant abonné à cet objet Context, le composant lira la valeur de contexte actuelle à partir du fournisseur correspondant le plus proche de lui dans l'arborescence des composants.
Ce n'est que lorsqu'il n'y a pas de fournisseur correspondant dans l'arborescence où se trouve le composant que son paramètre defaultValue prendra effet. Cela permet de tester les composants sans utiliser de fournisseur pour envelopper le composant. Remarque : Lorsque undefined est transmis à la valeur du fournisseur, la valeur par défaut du composant consommateur ne prendra pas effet.
Chaque objet Context renvoie un composant Provider React, qui permet aux composants consommateurs de s'abonner aux changements de contexte.
Le fournisseur reçoit un attribut de valeur et le transmet au composant consommateur. Un fournisseur peut avoir une relation correspondante avec plusieurs composants consommateurs. Plusieurs fournisseurs peuvent également être utilisés imbriqués et la couche interne écrasera les données de la couche externe.
Lorsque la valeur du fournisseur change, tous les composants consommateurs à l'intérieur seront restitués. Ni le fournisseur ni ses composants consommateurs internes ne sont soumis à la fonction ShouldComponentUpdate, de sorte que le composant consommateur peut être mis à jour même si son composant ancêtre quitte la mise à jour.
L'attribut contextType monté sur la classe sera réaffecté à un objet Context créé par React.createContext(). Cela vous permet d'utiliser this.context pour consommer la valeur du contexte le plus récent. Vous pouvez y accéder dans n'importe quel cycle de vie, y compris la fonction de rendu
importer MyContext depuis './MyContext';class MyClass extends React.Component { composantDidMount() { let value = this.context; /* Une fois le composant monté, utilisez la valeur du composant MyContext pour effectuer certaines opérations à effets secondaires* / } composantDidUpdate() { let value = this.context; /* ... */ } composantWillUnmount() { let value = this.context; /* ... */ } render() { let value = this.context ; /* Rendu basé sur la valeur du composant MyContext*/ } // Ou utilisez static contextType = MyContext;}MyClass.contextType = MyContext comme dans l'exemple ci-dessus.Ici, les composants React peuvent également s'abonner aux changements de contexte. Cela permet de s'abonner au contexte dans les composants fonctionnels.
Cela nécessite une fonction d’enfant. Cette fonction reçoit la valeur du contexte actuel et renvoie un nœud React. La valeur transmise à la fonction est équivalente à la valeur fournie par le fournisseur le plus proche de ce contexte dans l'arborescence des composants. S'il n'y a pas de fournisseur correspondant, le paramètre value est équivalent à la valeur defaultValue passée à createContext().
L'objet contextuel accepte une propriété nommée displayName, qui est de type chaîne. React DevTools utilise cette chaîne pour déterminer le contexte à afficher.
Le composant suivant apparaîtra sous le nom MyDisplayName dans DevTools
const MyContext = React.createContext(/* une valeur */);MyContext.displayName = 'MyDisplayName';<MyContext.Provider> // "MyDisplayName.Provider" dans DevTools <MyContext.Consumer> // "MyDisplayName.Consumer" Dans Outils de développementPour l'exemple de thème ci-dessus, une utilisation plus complexe peut être obtenue en utilisant des valeurs dynamiques.
thème-context.js
exporter const thèmes = { clair : { premier plan : '#000000', arrière-plan : '#eeeeee', }, sombre : { premier plan : '#ffffff', arrière-plan : '#222222', },};export const ThemeContext = React .createContext(themes.dark); // C'est la valeur par défautbouton-thème.js
import { ThemeContext } from './theme-context'; class ThemedButton extends React.Component { render() { let props = this.props; // Récupère la valeur par défaut dans ThemeContext let theme = this.context; {...props} style={{backgroundColor: theme.background}} /> ); } // static contextType = ThemeContext;}ThemedButton.contextType = ThemeContext;export ThemedButton par défaut;app.js
import { ThemeContext, thèmes } depuis './theme-context'; import ThemedButton depuis './themed-button';// Une fonction de composant intermédiaire utilisant ThemedButton Toolbar(props) { return ( <ThemedButton onClick={props.changeTheme } > Changer de thème </ThemedButton> );} class App extends React.Component { constructor(props) { super(props); this.state = { theme: theme.light, }; this.toggleTheme = () => { this .setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, })); } render() { // Utilisé par le composant de bouton ThemedButton dans ThemeProvider Le thème valeur dans l'état, // et les composants externes utilisent la valeur de thème par défaut renvoyée ( <Page> <ThemeContext.Provider value={this.state.theme}> <Toolbar changeTheme={this.toggleTheme} /> </ThemeContext .Provider> <Section> <ThemedButton /> </Section> </Page> ); }}ReactDOM.render(<App />, document.root);// Les composants enveloppés par ThemeContext.Provider peuvent consommer ThemeContext La valeur// dans la barre d'outils et ThemedButton peuvent utiliser this.context pour obtenir la valeur // Notez que la méthode de mise à jour de l'état consiste à le transmettre via des accessoires et que la mise à jour est déclenchée par les composants descendants. La méthode via le contexte sera discutée ci-dessous.Dans l'exemple ci-dessus, nous transmettons une fonction de mise à jour via des accessoires pour modifier la valeur des thèmes dans l'application. Nous savons qu'il est nécessaire de mettre à jour le contexte à partir d'un composant profondément imbriqué dans l'arborescence des composants. Dans ce scénario, vous pouvez transmettre une fonction via le contexte pour que le composant consumer mette à jour le contexte.
thème-context.js
// Assurez-vous que la structure de données de valeur par défaut transmise à createContext correspond aux composants appelants (consommateurs) ! export const ThemeContext = React.createContext({ theme: theme.dark, toggleTheme: () => {}, // Définir la méthode pour mettre à jour le thème, le transmettre});thème-toggler-button.js
import { ThemeContext } from './theme-context';function ThemeTogglerButton() { // Le bouton Theme Toggler obtient non seulement la valeur du thème, il obtient également une fonction toggleTheme à partir du contexte (partie app.js ci-dessous) return ( < ThemeContext.Consumer> {({theme, toggleTheme}) => ( <button onClick={toggleTheme} style={{backgroundColor: theme.background}}> Basculer le thème </button> )} </ThemeContext.Consumer> ); }exporter le ThemeTogglerButton par défaut ;app.js
importer { ThemeContext, thèmes } depuis './theme-context'; importer ThemeTogglerButton depuis './theme-toggler-button';class App extends React.Component { constructor(props) { super(props); ) => { this.setState(state => ({ theme: state.theme === Themes.dark ? Themes.light : Themes.dark, }) }; Sera transmis au fournisseur de contexte. this.state = { theme: theme.light, toggleTheme: this.toggleTheme, // Définir la fonction de mise à jour et la transmettre via le contexte }; } render() { // L'état entier est transmis au fournisseur return ( < ThemeContext. Provider value={this.state}> <Content /> </ThemeContext.Provider> ); }}function Content() { return ( <p> <ThemeTogglerButton /> </p> );}ReactDOM.render( <App />, document.root);Afin de garantir que le contexte soit restitué rapidement, React doit faire du contexte de chaque composant consommateur un nœud distinct dans l'arborescence des composants.
// Contexte du thème, le thème par défaut est la valeur "light" const ThemeContext = React.createContext('light'); // Contexte de connexion utilisateur const UserContext = React.createContext({ name: 'Guest',}); React .Component { render() { const { signéInUser, theme } = this.props; // Composant d'application qui fournit le retour de la valeur de contexte initiale ( <ThemeContext.Provider value={theme}> <UserContext.Provider value={signedInUser} > < Layout /> </UserContext.Provider> </ThemeContext.Provider> ); }}function Layout() { return ( <p> <Sidebar /> <Content /> </p> );}// Un composant peut consommer plusieurs fonctions contextuelles Content() { return ( <ThemeContext.Consumer> {theme => ( <UserContext.Consumer> {user => ( <ProfilePage user={user} theme={theme} /> )} </UserContext . Consommateur> )} </ThemeContext.Consumer> );}Si deux ou plusieurs valeurs de contexte sont fréquemment utilisées ensemble, vous souhaiterez peut-être envisager de créer votre propre composant de rendu pour fournir ces valeurs.
Étant donné que le contexte utilise une identité de référence pour décider du moment du rendu, certains pièges peuvent déclencher un rendu inattendu dans le composant consommateurs lorsque le composant parent du fournisseur effectue un nouveau rendu. Par exemple, chaque fois que le fournisseur est restitué, le code suivant restituera tous les composants consommateurs suivants, car l'attribut value est toujours affecté à un nouvel objet.
class App extends React.Component { render() { return ( <MyContext.Provider value={{something: 'something'}}> <Toolbar /> </MyContext.Provider> } );Afin d'éviter cette situation, l'état valeur est promu à l'état du nœud parent.
class App extends React.Component { constructor(props) { super(props); // Après plusieurs rendus, l'état sera conservé lorsque la valeur ne change pas, les composants consommateurs suivants ne restitueront pas this.state = {. valeur : {quelque chose : 'quelque chose'}, }; } render() { return ( <Provider value={this.state.value}> <Toolbar /> </Provider> }}