React中Context的API

1. Context

关于context官网文档有以下的描述:react

  1. If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
  2. If you aren't familiar with state management libraries like Redux or MobX, don't use context.
  3. If you aren't an experienced React developer, don't use context. There is usually a better way to implement functionality just using props and state.

综上所述就是不要使用context这个API。 虽说不要用,可是咱们也是要了解下这个API究竟是干吗的,毕竟有些优秀的库都是经过这个API实现而来,如:React-Redux。bash

简单了解context的做用就是在某个父组件中定义一个全局状态,这个状态能够在该父组件下的全部子组件中跨级传递共享。目前有两个版本分别是16.x以前和16.x以后的版本。app

2. 老版本的Context

在老版本中有以下几个方法:ide

getChildContext: 在父组件中声明一个函数,返回的结果是一个对象,这个对象就是context,能够对子组件进行共享的状态。函数

childContextTypes: 在父组件中声明,执行context中的数据类型,若是不指定会产生错误。ui

contextTypes: 在子孙组件中进行声明,指定要接受context中哪些数据类型。this

Tip: React.PropTypes has moved into a different package since React v15.5. Please use the prop-types library instead to define contextTypes.spa

如上,react15.5已经弃用React.PropTypes,须要安装prop-types库。code

看个小例子:component

//父组件
import React from 'react'
import DemoSun from './componets/DemoSun'
import propTyps from 'prop-types'

class Demo extends React.Component {

  getChildContext() {
    return {
      color: 'red'
    }
  }
  render() {
    return (
      <div>
        DEMO
        我是什么颜色的太阳:<DemoSun />
      </div>
    )
  }
}

Demo.childContextTypes = {
  color: propTyps.string
}

export default Demo 


//子组件
import React from 'react'
import propTyps from 'prop-types'

class DemoSun extends React.Component {

  render() {
    return (
      <div>
        DemoSun
        {this.context.color}
      </div>
    )
  }
}

DemoSun.contextTypes = {
  color: propTyps.string
}

export default DemoSun
复制代码

结果以下,子组件能够获取context中的color的值。

若是 contextTypes定义在某个组件中,则这个组件的生命周期函数中会增长一个参数:

constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
componentDidUpdate(prevProps, prevState, prevContext)
复制代码

若是在无状态组件中使用context则以下:

const PropTypes = require('prop-types');

const Button = ({children}, context) =>
  <button style={{background: context.color}}>
    {children}
  </button>;

Button.contextTypes = {color: PropTypes.string};
复制代码

关于老版的context就介绍到此,来关注下新版本的context。

3. 新版本的Context

新版本中使用ProviderConsumer模式,在顶层Provider中传入value,在子孙中的Consumer中获取该值,而且可以传递函数,用来修改context。

  • React.createContext(args):
const Mycontext = React.createContext(defaultValue)
复制代码

新版的是经过该方法初始化一个context对象。当React渲染了一个订阅了这个Context对象的组件,这个组件会从组件树中离自身最近的那个匹配Provider中读取到当前的context值。只有当组件所处的树中没有匹配到Provider时,其defaultValue参数才会生效。

  • Context.Provider
<Mycontext.Provider value={/*某个值*/}></Mycontext.Provider>
复制代码

每一个Context对象都会返回一个Provider组件。它容许消费组件订阅context变化。其有一个value属性,传递给消费组件。一个Provider能够和多个消费组件有对应关系,多个Provider也能够嵌套使用,里层的会覆盖外层数据。

当Provider的value值发生变化时,它内部的全部消费者组件都会从新渲染。Provider及其内部consumer组件都不受shouldComponentUpdate函数的影响,不管shouldComponentUpdate返回true或者false,所以当consumer组件在其祖先组件退出更新的状况下也能够更新。

  • Class.contexType

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

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;
复制代码
  • Context.Consumer
<MyContext.Consumer>
    {value=>/*基于context值进行渲染*/}
</MyContext.Consumer>
复制代码

这里,React组件也能够订阅到context变动。这能让你在函数式组件中完成订阅context。Consumer的children必须是一个函数。

这须要函数做为子元素这种作法。这个函数接受当前的context值,返回一个react节点。传递给函数的value值等同于往上组件树离这个context最近的Provider提供的value值。若是没有对应的Provider,value参数等于传递给createContext()的defaultValue。

4. 注意事项

context会使用参考标识来决定什么时候进行渲染。这样就会当provider的父组件进行从新渲染时,可能会在consumer组件中触发意外的渲染。以下:

class App extends React.Componenet{
    render() {
        return (
            <Provider value={{text: 'text'}}>
                <Demo />
            </Provider>
        )
    }
}
复制代码

如上,每次value都会建立一个新的对象。为了不这种状况,咱们能够将其提出到state中进行管理。

class App extends React.Componenet{
    constructor(props) {
        super(props)
        this.state = {
            text: 'text'
        }
    }
    render() {
        return (
            <Provider value={{text: this.state.text}}>
                <Demo />
            </Provider>
        )
    }
}
复制代码
相关文章
相关标签/搜索