从编程中的context来讲react - context(16.x

参考资料:javascript

  1. 编程中什么是「Context(上下文)」?.
  2. react - context官方文档.
  3. React Context(上下文) 做用和使用 看完不懂 你打我.
  4. React - Context 源码 github.

编程上的context

what is context ?

在这里插入图片描述

这是牛津词典里有关 context 的解释。一般咱们在编程当中会把 context 叫作 上下文,不过我认为这里彷佛叫作 语境更合适一些。可是不论是叫作 上下文或是 语境,都仍是有些抽象。html

从语文的角度理解

在学生时代,语文试卷中都会有个阅读理解部分,一般会有个问题相似于“结合上下文,x x x x x x x”,例如:java

..... 林冲大叫一声“啊也!” ..... 问:这句话林冲的“啊也”表达了林冲怎样的内心? 答:啊你妈个头啊!react

看,一篇文章,给你摘录一段,没前没后,你读不懂。由于有 语境,就是语言环境存在,一段话说了什么,要经过上下文(文章的上下文)来推断。git

从编程的角度理解

那么从编程的角度又该如何理解呢?一般咱们写代码的时候,除了一些简单的函数不须要外部变量,其余的复杂些的函数大多会依赖一些外部变量。而一旦须要外部变量,那么这段代码就不是完整的,不是可以独立运行的,而这里这些程序运行所必需的外部变量(也能够理解为前置条件),就叫作 上下文github

react - context

首先,react 在 16.3 引入了context,通过不过的迭代,到如今16.8.6的版本中, 官方对context的定义为 --- Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。归类于高级指引部分,属于react的高级api。web

什么时候使用context

一般在咱们写react组件的时候,数据传递通常采用单相数据流的方式,这样可使数据流向变得的简单而清晰。但在某些使用场景中须要共享一些对于一个组件树而言是**“全局”**的数据,这时就须要在每个层级手动修改传输的props,过于麻烦。 例如官方文档中提供的demo:编程

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

function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,而后传递给 ThemedButton 组件。
  // 若是应用中每个单独的按钮都须要知道 theme 的值,这会是件很麻烦的事,
  // 由于必须将这个值层层传递全部组件。
  return (
    <div>
      <ThemedButton theme={props.theme} />
    </div>
  );
}

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

在上述代码中,修改Button的theme,那么须要从App中修改theme,传给Toolbar,并由Toolbar传给ThemedButton使用,如此层层传递。若是ThemedButton与App以前还存在更多的嵌套关系,那么必需将theme在中间的各层级间层层传递,显然,这样过于繁琐了。redux

使用 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(props) {
  return (
    <div>
      <ThemedButton />
    </div>
  );
}

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

这是16.8.6版本中的文档demo(本文不对16.x以前的context使用方式作探讨,有兴趣同窗可自行研究)api

API

  1. 首先使用createContext方法建立一个Context对象。包含ProviderConsumer两个组件。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
const ThemeContext = React.createContext('light');
复制代码
  1. 使用**Provider(生产者)**组件来传递theme,圈定组件树范围,不管其内嵌套多深,任何组件都能读取theme。每一个 Context 对象都会返回一个 Provider React 组件,它容许消费组件订阅 context 的变化。
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给如下的组件树。
    // 不管多深,任何组件都能读取这个值。
    // 在这个例子中,咱们将 “dark” 做为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } 复制代码
  1. 当子孙组件须要使用theme时,指定contextType使用当前的Context
class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,而后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
  // 挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 建立的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。
    return <Button theme={this.context} />; } } 复制代码

4.中间无需使用theme的组件,不须要层层传递props

// 中间的组件不再必指明往下传递 theme 了。
function Toolbar(props) {
  return (
    <div> <ThemedButton /> </div>
  );
}
复制代码
  1. 3中的代码也可以使用Consumer(消费者), 他是专门消费Provider(生产者) 产生的数据。
class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,而后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
    return (
    	<ThemeContext.Consumer>
   	         //Consumer容器,能够拿到上文传递下来的 theme 属性,并能够展现对应的值
      		{(theme) =>
      			<Button theme={theme} />
            }
        </ThemeContext.Consumer>
    );
  }
}
复制代码

注意事项

1.由于每次Context值变动时,Consumer都会接受到相应的变化通知,这里可能会有一些陷阱,当 Provider 的父组件进行重渲染时,可能会在 Consumers 组件中触发意外的渲染。例如

class App extends React.Component {
  render() {
  //当每一次 Provider 重渲染时(即每次render时{something: 'something'}都指向一个新对象,拓展1),如下的代码会重渲染全部下面的 consumers 组件,由于 value 属性老是被赋值为新的对象
    return (
      <Provider value={{something: 'something'}}> <Toolbar /> </Provider>
    );
  }
}
复制代码

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

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <Provider value={this.state.value}> <Toolbar /> </Provider>
    );
  }
}
复制代码

2.组件的复用性下降

拓展

1.引用类型 2.mobx-react / react-router / react-redux 均适用context,有兴趣可自行探究。

相关文章
相关标签/搜索