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

相關文章
相關標籤/搜索