React中的Context怎么用

What is Context

今天在学习styled-componentsTheming时,关于styled-components对主题的实现与管理上提到,主要应用到了reactcontext API,因此好好研读了一下官方文档,对该API作了以下记录。html

什么是Context

当咱们使用React时,很容易的经过观察组件的props来跟踪组件间的数据流流向,这种跟踪观察方式也让咱们很容易的去理解组件。 react

而有的时候,咱们不想让一个props从最外层,经过组件一层一层的传递到目标组件上,这时就能够经过context来直接实现咱们但愿的操做。git

怎样使用Context

假设有个以下的结构:github

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.props.color}}>
        {this.props.children}
      </button>
    );
  }
}

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button color={this.props.color}>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  render() {
    const color = "purple";
    const children = this.props.messages.map((message) =>
      <Message text={message.text} color={color} />
    );
    return <div>{children}</div>;
  }
}

上面的例子中,咱们把color手动的方式传给了Button,这期间穿越了Message,而对Message自己没有什么用。若是用context的话,能够直接给到Button组件上,以下:redux

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

class Button extends React.Component {
  render() {
    return (
      <button style={{background: this.context.color}}>
        {this.props.children}
      </button>
    );
  }
}

Button.contextTypes = {
  color: PropTypes.string
};

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  getChildContext() {
    return {color: "purple"};
  }

  render() {
    const children = this.props.messages.map((message) =>
      <Message text={message.text} />
    );
    return <div>{children}</div>;
  }
}

MessageList.childContextTypes = {
  color: PropTypes.string
};

经过给MessageList(Context宿主)添加childContextTypesgetChildContext,能够实如今该组件子结构下的全部组件(e.g. Button)直接经过定义contextTypes来获取。 安全

若是未定义contextTypes的话,context是一个空对象。函数

可获取Context对象的勾子函数

一旦组件定义了contextTypes之后,如下的勾子中就会获得一个附加的参数——context对象:学习

无状态组件获取Context方法

无状态组件一样能够经过给函数定义contextTypes属性的方式,让组件拥有获取context的能力,例如:ui

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

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

Button.contextTypes = {color: PropTypes.string};

Context的更新

不要更新Context!this

React虽然有提供关于更新context的API,但不建议去使用。

若是想用的话,能够看下面的这个例子。
getChildContext方法会在stateprops更新时被调用,能够经过局部状态的更新进而来更新context。当context更新后,全部的子组件都能接到新值。

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

class MediaQuery extends React.Component {
  constructor(props) {
    super(props);
    this.state = {type:'desktop'};
  }

  getChildContext() {
    return {type: this.state.type};
  }

  componentDidMount() {
    const checkMediaQuery = () => {
      const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile';
      if (type !== this.state.type) {
        this.setState({type});
      }
    };

    window.addEventListener('resize', checkMediaQuery);
    checkMediaQuery();
  }

  render() {
    return this.props.children;
  }
}

MediaQuery.childContextTypes = {
  type: PropTypes.string
};

这里有个问题是,若是宿主组件的context更新了,其下使用该context的子组件可能由于某个父组件的shouldComponentUpdate返回false而不作状态更新。这就彻底不符合经过使用context来控制组件状态更新的初衷,因此证实使用context来管理组件状态不太靠谱。
这里有篇博客关于介绍如何安全的使用context的。

不建议使用Context

绝大多数的应用程序是不须要使用context的。

若是你想要你的应用稳定,就不要使用它,这是一个实验性的API,在将来的版本更新中颇有可能会被弃掉。

context最好的使用场景是隐式的传入登陆的用户,当前的语言,或者主题信息。要否则全部这些可能就是全局变量,可是context让你限定他们到一个单独的React树里。

若是项目对数据管理较为复杂,推荐使用相似于reduxmobX这样的状态管理库,而不要使用context

记录的过程是一种成长,欢迎你们关注个人github

相关文章
相关标签/搜索