首页前端开发其他前端知识react中context什么时候用?注意事项有什么

react中context什么时候用?注意事项有什么

时间2024-03-25 14:08:03发布访客分类其他前端知识浏览282
导读:今天这篇给大家分享的知识是“react中context什么时候用?注意事项有什么”,小编觉得挺不错的,对大家学习或是工作可能会有所帮助,对此分享发大家做个参考,希望这篇“react中context什么时候用?注意事项有什么”文章能帮助大家解...
今天这篇给大家分享的知识是“react中context什么时候用?注意事项有什么”,小编觉得挺不错的,对大家学习或是工作可能会有所帮助,对此分享发大家做个参考,希望这篇“react中context什么时候用?注意事项有什么”文章能帮助大家解决问题。

在react中,context是一个无需为每层组件手动添加props就能在组件树之间进行数据传递的方法;context提供了一种在组件之间共享指定值的方式,而且不必显式的通过组件树的逐层传递props。

本教程操作环境:Windows10系统、react17.0.1版、Dell G3电脑。

react中context是什么

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。在一个典型的 React 应用中,数据是通过 props 属性自上而下(由父及子)进行传递的,但这种做法对于某些类型的属性而言是极其繁琐的(例如:地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的。Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。

Context 什么时候用?

Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。举个例子,在下面的代码中,我们通过一个 “theme” 属性手动调整一个按钮组件的样式

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,显得很繁琐

使用 context, 我们可以避免通过中间元素传递 props

// 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;
    

API介绍

React.createContext

const MyContext = React.createContext(defaultValue);

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

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

Context.Provider

MyContext.Provider value={
/* 某个值 */}
    >
    

每个 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;
    

Context.Consumer

import MyContext from './MyContext';


function ToolList() {

  return (
    MyContext.Consumer
      {
    value =>
 /* 基于 context 值进行渲染*/}
    
    /MyContext.Consumer>

  )
}
    

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

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

Context.displayName

context 对象接受一个名为 displayName 的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。

如下述组件在 DevTools 中将显示为 MyDisplayName

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

MyContext.Provider>
       // "MyDisplayName.Provider" 在 DevTools 中
MyContext.Consumer>
   // "MyDisplayName.Consumer" 在 DevTools 中

示例

动态 Context

对于上面的 theme 例子,使用动态值(dynamic values)后更复杂的用法

theme-context.js

export const themes = {

  light: {

    foreground: '#000000',
    background: '#eeeeee',
  }
,
  dark: {

    foreground: '#ffffff',
    background: '#222222',
  }
,
}
    ;
    

export const ThemeContext = React.createContext(themes.dark);
   // 该处为默认值

themed-button.js

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;

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的方式传递更新函数

在嵌套组件中更新 Context

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

theme-context.js

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

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;

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);
    

消费多个 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>
    
  );

}

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

注意事项

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

class App extends React.Component {

  render() {

    return (
      MyContext.Provider value={
{
something: 'something'}
}
    >
    
        Toolbar />
    
      /MyContext.Provider>
    
    );

  }

}

为了防止这种情况,将 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>
    
    );

  }

}
    

关于“react中context什么时候用?注意事项有什么”的内容就介绍到这,感谢各位的阅读,相信大家对react中context什么时候用?注意事项有什么已经有了进一步的了解。大家如果还想学习更多知识,欢迎关注网络,小编将为大家输出更多高质量的实用文章!

声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!

React

若转载请注明出处: react中context什么时候用?注意事项有什么
本文地址: https://pptw.com/jishu/652829.html
用PHP怎么样实现翻译功能,方法是什么? 在node.js中await的使用及注意事项是什么

游客 回复需填写必要信息