Maison > interface Web > Questions et réponses frontales > Qu'est-ce que le contexte en réaction

Qu'est-ce que le contexte en réaction

WBOY
Libérer: 2022-06-28 10:55:37
original
2504 Les gens l'ont consulté

Dans React, le contexte est une méthode permettant de transférer 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 des valeurs spécifiées entre les composants sans avoir à transmettre explicitement les accessoires couche par couche à travers l'arborescence des composants. .

Qu'est-ce que le contexte en réaction

L'environnement d'exploitation de ce tutoriel : système Windows 10, React version 17.0.1, ordinateur Dell G3.

Qu'est-ce que le contexte dans React ?

Context fournit une méthode pour transférer 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.

Contexte Quand utiliser ?

Context est conçu pour partager des données « globales » avec une arborescence de composants, telles que l'utilisateur, le thème ou la langue préférée actuellement authentifié. Par exemple, dans le code ci-dessous, nous ajustons manuellement le style d'un composant de bouton via un attribut "thème"

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  return (
    <p>
      <ThemedButton theme={props.theme} />
    </p>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

// 通过props传递:App -> Toolbar -> ThemedButton
// 如果嵌套很深,那么需要逐层传递props,即使中间不需要该props,显得很繁琐
Copier après la connexion

En utilisant le contexte, nous pouvons éviter de passer des accessoires via des éléments intermédiaires

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context("light"为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
  return (
    <p>
      <ThemedButton />
    </p>
  );
}

class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
// 也可以使用 ThemedButto.contextType = ThemeContext;
Copier après la connexion

Introduction à l'API

React.createContextReact.createContext

const MyContext = React.createContext(defaultValue);
Copier après la connexion

创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。

只有当组件所处的树中没有匹配到 Provider 时,其 defaultValue 参数才会生效。这有助于在不使用 Provider 包装组件的情况下对组件进行测试。注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

Context.Provider

<MyContext.Provider value={/* 某个值 */}>
Copier après la connexion

每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化。

Provider 接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。

当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。Provider 及其内部 consumer 组件都不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件退出更新的情况下也能更新。

Class.contextType

挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中

import MyContext from './MyContext';

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
  // 或者如上边例子一样使用 static contextType = MyContext;
}
MyClass.contextType = MyContext;
Copier après la connexion

Context.Consumer

import MyContext from './MyContext';

function ToolList() {
  return (
    <MyContext.Consumer
      {value => /* 基于 context 值进行渲染*/}
    </MyContext.Consumer>
  )
}
Copier après la connexion

这里,React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context。

这需要函数作为子元素(function as a child)这种做法。这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue

Context.displayName

context 对象接受一个名为 displayName

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider>   // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer>   // "MyDisplayName.Consumer" 在 DevTools 中
Copier après la connexion

Créez un objet Context. Lorsque React restitue un composant abonné à cet objet Context, le composant lira la valeur de contexte actuelle à partir du Provider correspondant le plus proche de lui dans l'arborescence des composants.

Seulement lorsqu'il n'y a pas de fournisseur correspondant dans l'arborescence où se trouve le composant, son paramètre defaultValue prendra effet. Cela permet de tester les composants sans les envelopper avec un fournisseur. Remarque : Lorsque vous transmettez undefined à la valeur du fournisseur, le defaultValue du composant consommateur ne prendra pas effet.

Context.Provider
export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(themes.dark);   // 该处为默认值
Copier après la connexion
Chaque objet Context renvoie un composant Provider React, qui permet aux composants consommateurs de s'abonner aux changements de contexte.

Provider reçoit un attribut value 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 value du fournisseur change, tous les composants consommateurs à l'intérieur seront restitués. Ni le fournisseur ni son composant consommateur interne 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.

Class.contextType

L'attribut contextType monté sur la classe sera réaffecté à celui créé par l'objet Contexte React.createContext(). Cela vous permet d'utiliser this.context pour consommer la valeur sur le contexte le plus récent. Vous pouvez y accéder dans n'importe quel cycle de vie, y compris dans la fonction de rendu

import { ThemeContext } from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    // 获取到ThemeContext中的默认值
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
  // static contextType = ThemeContext;
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;
Copier après la connexion

Context.Consumer

import { ThemeContext, themes } from './theme-context';
import ThemedButton from './themed-button';

// 一个使用 ThemedButton 的中间组件
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };
  }

  render() {
    // 在 ThemeProvider 内部的 ThemedButton 按钮组件使用 state 中的 theme 值,
    // 而外部的组件使用默认的 theme 值
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

ReactDOM.render(<App />, document.root);

// 使用ThemeContext.Provider包裹的组件,可以消费到ThemeContext中的value
// 即Toolbar、ThemedButton中都可以使用this.context来获取到value
// 注意观察,更新state的方法是通过props向下传递,由子孙组件触发更新,下面会讲到通过context的方式传递更新函数
Copier après la connexion
Copier après la connexion
Ici, les composants React peuvent également s'abonner aux changements de contexte. Cela vous permet de vous abonner au contexte dans les composants fonctionnels. Cela nécessite une fonction en tant qu'enfant. Cette fonction reçoit la valeur du contexte actuel et renvoie un nœud React. La valeur value transmise à la fonction est équivalente à la valeur value 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 au defaultValue passé à createContext().

Context.displayNameL'objet contexte accepte une propriété nommée displayName, de type string. React DevTools utilise cette chaîne pour déterminer le contexte à afficher.

Le composant suivant sera affiché sous le nom MyDisplayName dans DevTools

// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},   // 定义更新主题的方法,向下传递
});
Copier après la connexion
Copier après la connexion

🎜Exemple🎜🎜🎜🎜🎜Contexte dynamique🎜🎜🎜Pour l'exemple de thème ci-dessus, utilisez des valeurs dynamiques pour une utilisation plus complexe🎜🎜🎜theme -context. js🎜🎜
import { ThemeContext } from './theme-context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数(下面app.js部分)
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;
Copier après la connexion
Copier après la connexion
🎜🎜themed-button.js🎜🎜
import { ThemeContext, themes } from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };

    // State 也包含了更新函数,因此它会被传递进 context provider。
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,   // 定义更新函数,通过context方式向下传递
    };
  }

  render() {
    // 整个 state 都被传递进 provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  return (
    <p>
      <ThemeTogglerButton />
    </p>
  );
}

ReactDOM.render(<App />, document.root);
Copier après la connexion
Copier après la connexion
🎜🎜app.js🎜🎜
import { ThemeContext, themes } from './theme-context';
import ThemedButton from './themed-button';

// 一个使用 ThemedButton 的中间组件
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };
  }

  render() {
    // 在 ThemeProvider 内部的 ThemedButton 按钮组件使用 state 中的 theme 值,
    // 而外部的组件使用默认的 theme 值
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

ReactDOM.render(<App />, document.root);

// 使用ThemeContext.Provider包裹的组件,可以消费到ThemeContext中的value
// 即Toolbar、ThemedButton中都可以使用this.context来获取到value
// 注意观察,更新state的方法是通过props向下传递,由子孙组件触发更新,下面会讲到通过context的方式传递更新函数
Copier après la connexion
Copier après la connexion

在嵌套组件中更新 Context

在上面的例子中,我们通过 props 的方式向下传递一个更新函数,从而改变 App 中 themes 的值。我们知道,从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context

theme-context.js

// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},   // 定义更新主题的方法,向下传递
});
Copier après la connexion
Copier après la connexion

theme-toggler-button.js

import { ThemeContext } from './theme-context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数(下面app.js部分)
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;
Copier après la connexion
Copier après la connexion

app.js

import { ThemeContext, themes } from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };

    // State 也包含了更新函数,因此它会被传递进 context provider。
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,   // 定义更新函数,通过context方式向下传递
    };
  }

  render() {
    // 整个 state 都被传递进 provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  return (
    <p>
      <ThemeTogglerButton />
    </p>
  );
}

ReactDOM.render(<App />, document.root);
Copier après la connexion
Copier après la connexion

消费多个 Context

为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点

// Theme context,默认的 theme 是 "light" 值
const ThemeContext = React.createContext('light');

// 用户登录 context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const { signedInUser, theme } = this.props;

    // 提供初始 context 值的 App 组件
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Layout() {
  return (
    <p>
      <Sidebar />
      <Content />
    </p>
  );
}

// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}
Copier après la connexion

如果两个或者更多的 context 值经常被一起使用,那你可能要考虑一下另外创建你自己的渲染组件,以提供这些值。

注意事项

因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: &#39;something&#39;}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}
Copier après la connexion

为了防止这种情况,将 value 状态提升到父节点的 state 里

class App extends React.Component {
  constructor(props) {
    super(props);
    // 多次渲染,state 会被保留,当value不变时,下面的 consumers 组件不会重新渲染 
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}
Copier après la connexion

【相关推荐:javascript视频教程web前端

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal