【React】Context


Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

Context 提供了一种在组件之间共享此类值的方式,而不必显式地通过组件树的逐层传递 props。


1. 何时使用Context

Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据。

例如当前认证的用户、主题或首选语言。

举个例子,在下面的代码中,我们通过一个 “theme” 属性手动调整一个按钮组件的样式:

  • App.js
import './App.css';
import Toolbar from './component/Toolbar';

function App() {
  return (
    <div className="App">
      <Toolbar theme='dark'/>
    </div>
  );
}

export default App;
  • Toolbar.js
import ThemedButton from './ThemedButton';

function Toolbar(props){
    return (
        <>
            <ThemedButton theme={props.theme}/>
        </>
    )
}

export default Toolbar;
  • ThemedButton.js
import React from 'react';

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

export default ThemedButton;

Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
因为必须将这个值层层传递所有组件。

使用context,避免通过中间元素传递props:

  • App.js
import './App.css';
import Toolbar from './component/Toolbar';
import React from 'react';

// Context可以让我们无需明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的theme创建一个context('light'为默认值)
export const ThemeContext = React.createContext('light');

function App() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
  return (
    <div className="App">
        <ThemeContext.Provider value='dark'>
            <Toolbar theme='dark'/>
        </ThemeContext.Provider>
    </div>
  );
}

export default App;
  • Toolbar.js
import ThemedButton from './ThemedButton';

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

export default Toolbar;
  • ThemedButton.js
import React from 'react';
import ThemeContext from '../App';

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

export default ThemedButton;

2. API

2.1 React.createContext

const MyContext = React.createContext(defaultValue);
  • 创建一个Context对象。
  • 当React渲染一个组件(订阅了Context对象),这个组件会从组件树中离自身最近的匹配的Provider中读取当前context值。
  • 只有当组件所处的树没有匹配到Provider时,defaultValue参数才会生效。(此默认值有助于在不使用 Provider 包装组件的情况下对组件进行测试。)
  • 注意:将 undefined 传递给 Provider 的 value 时,消费组件的 defaultValue 不会生效。

2.2 Context对象.Provider

<MyContext.Provider value={/* 某个值 */}>
  • 每一个Context对象都会返回一个Provide React组件,它允许消费组件订阅context的变化。
  • Provider接收一个value属性,传递给消费组件。
  • 一个Provider可以和多个消费组件有对应关系。多个Provider也可以嵌套使用,里层的会覆盖外层的数据。
  • 当Provider的value值发生变化时,它内部的所有消费组件都会重新渲染。
  • 从 Provider 到其内部 consumer 组件(包括 .contextType 和 useContext)的传播不受制于 shouldComponentUpdate 函数,因此当 consumer 组件在其祖先组件跳过更新的情况下也能更新。
  • 通过新旧值检测来确定变化。

2.3 Class.contextType

类组件的使用方式,函数组件使用钩子函数useContext()南栀的博客–Hooks

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 组件的值进行渲染 */
  }
}
MyClass.contextType = MyContext;
  • 挂载在class上的contextType属性可以赋值为由React.createContext()创建的Context对象。
  • contextType可以让你使用this.context来获取最近Context上的值。你可以在任何生命周期中访问到它,包括 render 函数中。

2.4 Context对象.Consumer

<MyContext.Consumer>
  {value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
  • 一个 React 组件可以订阅 context 的变更,此组件可以让你在函数式组件中可以订阅 context。
  • 这种方法需要一个函数作为子元素(function as a child)。这个函数接收当前的 context 值,并返回一个 React 节点。
  • 传递给函数的 value 值等价于组件树上方离这个 context 最近的 Provider 提供的 value 值。
  • 如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。

2.5 Context对象.displayName

context 对象接受一个名为 displayName 的property,类型为字符串。

React DevTools 使用该字符串来确定 context 要显示的内容。

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

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

3. 示例

3.1 动态Context

  • theme-context.js
import React from 'react';

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

export const ThemeContext = React.createContext(themes.dark);
  • themed-button.js
import {ThemeContext} from '../theme-context';
import React from 'react';

class ThemedButton extends React.Component {
    render(){
        let props = this.props;
        let theme = this.context;
        return(
            <button
                {...props} style={{background:theme.background}}>

            </button>
        )
    }
}

ThemedButton.contextType = ThemeContext;

export default ThemedButton;
  • App.js
import './App.css';
import {ThemeContext,themes} from './theme-context';
import ThemedButton from './component/themed-button';
import React from 'react';

// 一个使用 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 (
        <>
          <ThemeContext.Provider value={this.state.theme}>
            <Toolbar changeTheme={this.toggleTheme} />
          </ThemeContext.Provider>
          <>
            <ThemedButton />
          </>
        </>
    );
  }
}

export default App;

在这里插入图片描述
参考文档:React官方文档

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南栀~zmt

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值