今天在學習styled-components的Theming
時,關於styled-components
對主題的實現與管理上提到,主要應用到了react
的context API,因此好好研讀了一下官方文檔,對該API作了以下記錄。html
當咱們使用React
時,很容易的經過觀察組件的props
來跟蹤組件間的數據流流向,這種跟蹤觀察方式也讓咱們很容易的去理解組件。 react
而有的時候,咱們不想讓一個props
從最外層,經過組件一層一層的傳遞到目標組件上,這時就能夠經過context
來直接實現咱們但願的操做。git
假設有個以下的結構: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宿主)添加childContextTypes
和getChildContext
,能夠實如今該組件子結構下的全部組件(e.g. Button)直接經過定義contextTypes
來獲取。 安全
若是未定義contextTypes
的話,context
是一個空對象。函數
一旦組件定義了contextTypes
之後,如下的勾子中就會獲得一個附加的參數——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!this
React
雖然有提供關於更新context
的API,但不建議去使用。
若是想用的話,能夠看下面的這個例子。getChildContext
方法會在state
或props
更新時被調用,能夠經過局部狀態的更新進而來更新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
的。
若是你想要你的應用穩定,就不要使用它,這是一個實驗性的API,在將來的版本更新中頗有可能會被棄掉。
context最好的使用場景是隱式的傳入登陸的用戶,當前的語言,或者主題信息。要否則全部這些可能就是全局變量,可是context讓你限定他們到一個單獨的React樹裏。
若是項目對數據管理較爲複雜,推薦使用相似於redux或mobX這樣的狀態管理庫,而不要使用context
。
記錄的過程是一種成長,歡迎你們關注個人github。