React中,經過React組件能夠很容易地追蹤數據流。當你關注一個組件,你能夠發現哪個props被傳遞了,這樣使得你的應用很容被推斷。react
在一些狀況下,你想要傳遞數據經過組件樹而不須要去手動在每一層傳遞。你能夠直接使用強大的context API。react-router
爲何不使用contextdom
大量的應用不須要使用context。函數
若是你但願你的應用穩定,不要使用context。這是一個實驗性的API在將來的版本中有可能會崩潰。this
若是你不熟悉state管理庫就相似於Redux或者Mobx,不要使用context。對於不少實際的應用,這些庫和它們的React綁定實現是很好的選擇來管理state,這對於不少組件都有重大意義。解決問題最好的方案更像是Redux而不是context。spa
若是你不是一個有經驗的React開發者,不要使用context。有更好的方式去實現功能經過使用props和state。code
若是你堅持使用context而無論這些警告,那麼就請試圖隔離你使用context在一個較小的範圍而且避免直接使用context API爲了當API改變的時候升級方便。component
怎樣使用contextrouter
假設你擁有這樣的一個結構:對象
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組件的有一個合適的樣式。使用context,咱們能夠自動傳遞屬性經過樹。
class Button extends React.Component { render() { return ( <button style={{background: this.context.color}}> {this.props.children} </button> ); } } Button.contextTypes = { color: React.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: React.PropTypes.string };
經過爲MessageList組件(context提供者)添加childContextTypes屬性和getChildContext方法,React會自動傳遞信息而且任何子樹裏的組件(在這個例子,Button組件)均可以獲取到這個信息經過定義contextTypes。
若是contextTypes沒有定義,那麼context會是一個空對象。
父子聯合
context也可讓你建造一套可讓父組件和子組件通訊的API。舉個例子,一個這樣運做的庫叫作React Router v4:
import { Router, Route, Link } from 'react-router-dom'; const BasicExample = () => ( <Router> <div> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/topics">Topics</Link></li> </ul> <hr /> <Route exact path="/" component={Home} /> <Route path="/about" component={About} /> <Route path="/topics" component={Topics} /> </div> </Router> );
經過從Router組件傳遞一些信息,每個Link和Route均可以和包含的Router通訊。
在你使用相似的API建立組件的時候,思考是否有更靈活的替代方案。舉個例子,你能夠傳遞整個React組件做爲props若是你喜歡。
在生命週期方法裏引用context
若是在一個組件內部定義了contextTypes,下面的生命週期方法將會接收一個額外的參數,就是context對象:
constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
componentDidUpdate(prevProps, prevState, prevContext)
在無狀態的函數式組件裏引用context
const Button = ({children}, context) => <button style={{background: context.color}}> {children} </button>; Button.contextTypes = {color: React.PropTypes.string};
更新context
不要這樣作。
React有一個更新context的API,可是它實質上已經損壞你不該該使用它。
getChildContext函數當props或者state改變的時候會被調用。爲了在context中更新數據,使用this.setState來觸發本地的state更新。這樣將會觸發一個新的context而且改變會被子組件接收。
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: React.PropTypes.string };
問題在於,若是一個組件提供的context值改變了,使用那個值的子節點就不會更新若是中間的組件從shouldComponentUpdate返回了false。這樣組件使用context就徹底失去了控制,所以基本沒有什麼方法能夠可靠地更新context。這個博客有一個很好地解釋關於爲何這是一個問題以及你怎樣避開它。