React.js 的 context

这一节咱们来介绍一个你可能永远用不上的 React.js 特性 —— context。可是了解它对于了解接下来要讲解的 React-redux 颇有好处,因此你们能够简单了解一下它的概念和做用。html

在过去很长一段时间里面,React.js 的 context 一直被视为一个不稳定的、危险的、可能会被去掉的特性而不被官网文档所记载。可是全世界的第三方库都在用使用这个特性,直到了 React.js 的 v0.14.1 版本,context 才被官方文档所记录。前端

除非你以为本身的 React.js 水平已经比较炉火纯青了,不然你永远不要使用 context。就像你学 JavaScript 的时候,老是会被提醒不要用全局变量同样,React.js 的 context 其实像就是组件树上某颗子树的全局变量。redux

想象一下咱们有这么一棵组件树:函数

假设如今这个组件树表明的应用是用户能够自主换主题色的,每一个子组件会根据主题色的不一样调整本身的字体颜色或者背景颜色。“主题色”这个玩意是全部组件共享的状态,根据咱们在 前端应用状态管理 —— 状态提高 中所提到的,须要把这个状态提高到根节点的 Index 上,而后把这个状态经过 props 一层层传递下去:字体

假设原来主题色是绿色,那么 Index 上保存的就是 this.state = { themeColor: 'green' }。若是要改变主题色,能够直接经过 this.setState({ themeColor: 'red' })来进行。这样整颗组件树就会从新渲染,子组件也就能够根据从新传进来的 props.themeColor 来调整本身的颜色。this

但这里的问题也是很是明显的,咱们须要把 themeColor 这个状态一层层手动地从组件树顶层往下传,每层都须要写 props.themeColor。若是咱们的组件树很层次很深的话,这样维护起来简直是灾难。spa

若是这颗组件树可以全局共享这个状态就行了,咱们要的时候就去取这个状态,不用手动地传:code

就像这样,Index 把 state.themeColor 放到某个地方,这个地方是每一个 Index 的子组件均可以访问到的。当某个子组件须要的时候就直接去那个地方拿就行了,而不须要一层层地经过 props 来获取。无论组件树的层次有多深,任何一个组件均可以直接到这个公共的地方提取 themeColor 状态。component

React.js 的 context 就是这么一个东西,某个组件只要往本身的 context 里面放了某些状态,这个组件之下的全部子组件都直接访问这个状态而不须要经过中间组件的传递。一个组件的 context 只有它的子组件可以访问,它的父组件是不能访问到的,你能够理解每一个组件的 context 就是瀑布的源头,只能往下流不能往上飞。htm

咱们看看 React.js 的 context 代码怎么写,咱们先把总体的组件树搭建起来,这里不涉及到 context 相关的内容:

class Index extends Component {
  render () {
    return (
      <div>
        <Header />
        <Main />
      </div>
    )
  }
}

class Header extends Component {
  render () {
    return (
    <div>
      <h2>This is header</h2>
      <Title />
    </div>
    )
  }
}

class Main extends Component {
  render () {
    return (
    <div>
      <h2>This is main</h2>
      <Content />
    </div>
    )
  }
}

class Title extends Component {
  render () {
    return (
      <h1>React.js 小书标题</h1>
    )
  }
}

class Content extends Component {
  render () {
    return (
    <div>
      <h2>React.js 小书内容</h2>
    </div>
    )
  }
}

ReactDOM.render(
  <Index />,
  document.getElementById('root')
)

代码很长可是很简单,这里就不解释了。

如今咱们修改 Index,让它往本身的 context 里面放一个 themeColor

class Index extends Component {
  static childContextTypes = {
    themeColor: PropTypes.string
  }

  constructor () {
    super()
    this.state = { themeColor: 'red' }
  }

  getChildContext () {
    return { themeColor: this.state.themeColor }
  }

  render () {
    return (
      <div>
        <Header />
        <Main />
      </div>
    )
  }
}

构造函数里面的内容其实就很好理解,就是往 state 里面初始化一个 themeColor 状态。getChildContext 这个方法就是设置 context 的过程,它返回的对象就是 context(也就是上图中处于中间的方块),全部的子组件均可以访问到这个对象。咱们用 this.state.themeColor 来设置了 context 里面的 themeColor

还有一个看起来很可怕的 childContextTypes,它的做用其实 propsType 验证组件 props 参数的做用相似。不过它是验证 getChildContext 返回的对象。为何要验证 context,由于 context 是一个危险的特性,按照 React.js 团队的想法就是,把危险的事情搞复杂一些,提升使用门槛人们就不会去用了。若是你要给组件设置 context,那么 childContextTypes 是必写的。

如今咱们已经完成了 Index 往 context 里面放置状态的工做了,接下来咱们要看看子组件怎么获取这个状态,修改 Index 的孙子组件 Title

class Title extends Component {
  static contextTypes = {
    themeColor: PropTypes.string
  }

  render () {
    return (
      <h1 style={{ color: this.context.themeColor }}>React.js 小书标题</h1>
    )
  }
}

子组件要获取 context 里面的内容的话,就必须写 contextTypes 来声明和验证你须要获取的状态的类型,它也是必写的,若是你不写就没法获取 context 里面的状态。Title 想获取 themeColor,它是一个字符串,咱们就在 contextTypes 里面进行声明。

声明之后咱们就能够经过 this.context.themeColor 获取到在 Index 放置的值为 red 的 themeColor,而后设置 h1 的样式,因此你会看到页面上的字体是红色的:

若是咱们要改颜色,只须要在 Index 里面 setState 就能够了,子组件会从新渲染,渲染的时候会从新取 context 的内容,例如咱们给 Index 调整一下颜色:

...
  componentWillMount () {
    this.setState({ themeColor: 'green' })
  }
...

那么 Title 里面的字体就会显示绿色。咱们能够如法炮制孙子组件 Content,或者任意的 Index 下面的子组件。让它们能够不通过中间 props 的传递获就能够获取到由 Index 设定的 context 内容。

总结

一个组件能够经过 getChildContext 方法返回一个对象,这个对象就是子树的 context,提供 context 的组件必须提供 childContextTypes 做为 context 的声明和验证。

若是一个组件设置了 context,那么它的子组件均可以直接访问到里面的内容,它就像这个组件为根的子树的全局变量。任意深度的子组件均可以经过 contextTypes 来声明你想要的 context 里面的哪些状态,而后能够经过 this.context 访问到那些状态。

context 打破了组件和组件之间经过 props 传递数据的规范,极大地加强了组件之间的耦合性。并且,就如全局变量同样,context 里面的数据能被随意接触就能被随意修改,每一个组件都可以改 context 里面的内容会致使程序的运行不可预料。

可是这种机制对于前端应用状态管理来讲是颇有帮助的,由于毕竟不少状态都会在组件之间进行共享,context 会给咱们带来很大的方便。一些第三方的前端应用状态管理的库(例如 Redux)就是充分地利用了这种机制给咱们提供便利的状态管理服务。但咱们通常不须要手动写 context,也不要用它,只须要用好这些第三方的应用状态管理库就好了。

相关文章
相关标签/搜索