使用React能夠很容易經過React組件跟蹤數據流。 當你看到一個組件,你就能夠看到哪些props
正在傳遞,這使得你的應用很容易知道在作什麼。javascript
在某些狀況下,你但願經過組件樹傳遞數據,而不是在每一個級別的中組件手動傳遞props
,你能夠直接在React中使用強大的「context」
來作到這一點。java
絕大多數的應用不須要直接使用Context。react
若是你但願你的應用是穩定的,那麼就不要使用Context。 由於這是一個實驗性質的API,它可能會在將來的React版本中移除。dom
若是你不熟悉state管理庫如Redux
或MobX
,不要使用Context。 對於許多實際的應用,這些state管理庫和React一塊兒使用來管理那些與組件相關的state一個很不錯的選擇。 不少狀況下使用Redux
就能夠解決你的問題,而不是使用Context。函數
若是你不是一個有經驗的React開發人員,不要使用Context。 使用props
和state
來實現功能是一個更好的方法。ui
儘管有上面這些警告你還堅持使用Context,那麼請將Context單獨隔離到一個小區域中,並儘量地避免直接使用Context,以便在這個API更改時應用能更容易升級。this
假設你有一個結構:code
import React from 'react'; import ReactDOM from 'react-dom'; class MyButton extends React.Component { constructor(props) { super(props); } render() { const style = {backgroundColor: this.props.color}; return <button style={style}>{this.props.children}</button>; } } class Message extends React.Component { render() { return ( <div> {this.props.text} <MyButton color={this.props.color}>刪除</MyButton> </div> ) } } class MessageList extends React.Component { render() { const color = 'red'; const children = this.props.messages.map(msg => <Message text={msg} color={color}/>); return <div>{children}</div>; } } const messages = ['zhangyato', 'Re: zhangyatao', 'Re:Re:zhangyatao']; ReactDOM.render( <MessageList messages={messages}/>, document.getElementById('root') );
在這個例子中,咱們手動傳入一個color
props進行傳遞,以便適當地設置MyButton
和Message
組件的樣式。 使用Context,咱們可讓color
自動在組件樹中傳遞:component
import React from 'react'; import ReactDOM from 'react-dom'; class MyButton extends React.Component { constructor(props) { super(props); } render() { const style = {backgroundColor: this.context.color}; return <button style={style}>{this.props.children}</button>; } } MyButton.contextTypes = { color: React.PropTypes.string.isRequired }; class Message extends React.Component { render() { return ( <div> {this.props.text} <MyButton>刪除</MyButton> </div> ) } } class MessageList extends React.Component { getChildContext() { return {color: 'red'}; } render() { const children = this.props.messages.map(msg => <Message text={msg}/>); return <div>{children}</div>; } } MessageList.childContextTypes = { color: React.PropTypes.string.isRequired }; const messages = ['zhangyato', 'Re: zhangyatao', 'Re:Re:zhangyatao']; ReactDOM.render( <MessageList messages={messages}/>, document.getElementById('root') );
經過向MessageList
(Context提供者)添加childContextTypes
和getChildContext
,React就會自動傳遞Context信息,子組件樹中的任何組件(Button
)均可以經過定義contextTypes
來訪問它。對象
若是未定義contextTypes
,那麼Context將是一個空對象。
Context還可讓你實現父組件和子組件之間的交互。 例如,以這種方式工做的一個比較知名的庫爲React Router V4
:
const RouterExample = () => { <Router> <div> <ul> <li><Link to="/">主頁</Link></li> <li><Link to="/recyclebin">回收站</Link></li> <li><Link to="/timeline">圖片</Link></li> </ul> <hr /> <Match exactly pattern="/" component={Home} /> <Match pattern="/recyclebin" component={RecycleBin} /> <Match pattern="/timeline" component={TimeLine} /> </div> </Router> }
經過從RouterExample
組件傳遞一些信息,每一個Link
和Match
能夠回傳到包含的Router
中。
在使用相似於此的API進行構建組件以前,請考慮是否有更好的替代品。 例如,你能夠傳遞整個React組件做爲props。
若是在一個組件中定義了contextTypes
,則如下生命週期方法將多接收個參數,就是Context對象:
constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
componentDidUpdate(prevProps, prevState, prevContext)
若是contextTypes
被定義爲無狀態功能性組件的屬性,無狀態功能性組件也可以引用Context。 下面的代碼顯示了被寫成一個無狀態的功能組件的MyButton
組件。
function MyButton(props, context) { const children = props.children; return ( <button style={{backgroundColor: context.color}}> {children} </button> ); } MyButton.contextTypes = { color: React.PropTypes.string.isRequired };
千萬不要這麼作!!!
React有一個API來更新Context,但它從根本上破壞了Context,因此你不該該使用它。
當state或props改變時,getChildContext
函數將被調用。 爲了更新Context中的數據,使用this.setState()
觸發組件內的state更新。 這也將觸發一個新的Context,而且Context改變的內容也會被子組件接收到。
import React from 'react'; import ReactDOM from 'react-dom'; class MediaType extends React.Component { render() { return <div>type is {this.context.type}</div> } } MediaType.contextTypes = { type: React.PropTypes.string }; class MediaQuery extends React.Component { constructor(props) { super(props); this.state = {type: 'PC端'}; this.checkMediaQuery = this.checkMediaQuery.bind(this); } getChildContext() { return {type: this.state.type} } checkMediaQuery() { let type = window.matchMedia('<code>zhangyatao</code>(max-width: 760px)').matches ? '移動端' : 'PC端'; this.setState({type: type}); } componentDidMount() { window.addEventListener('resize', this.checkMediaQuery, false); this.checkMediaQuery(); } render() { return <div>{this.props.children}</div>; } } MediaQuery.childContextTypes = { type: React.PropTypes.string }; ReactDOM.render( <MediaQuery> <MediaType /> </MediaQuery>, document.getElementById('root') );
問題是,Context的值經過組件更新來提供,若是中間的父組件在shouldComponentUpdate()
返回false,那麼使用該Context值的後代組件永遠不會被更新。使用Context徹底不受組件的控制,因此基本上沒有辦法可靠地更新Context。 這篇文章很好的解釋了爲何這是一個問題,以及你應該如何擺脫它。