上下文(Context) 提供了在組件之間共享這些值的方法,而沒必要在樹的每一個層級顯式傳遞一個 prop 。javascript
通常狀況下,在你沒有絕對把握用好和必須的場景下,是不推薦使用的。java
可是咱們依然間接的使用着它,好比許多官方依賴在使用,如:react-redux, mobx-react,react-router。咱們須要它功能的時候,更可能是靠第三方依賴庫就能實現,而不是本身手動寫context。可是,依然須要理解它,對用它的一些依賴庫源碼理解頗有幫助。react
import React, { Component } from 'react';
class Grandpa extends Component {
state = {
info: 'GrandPa組件的信息'
}
render() {
return (
<div>
<Father info={this.state.info}/>
</div>
);
}
}
class Father extends Component {
render() {
return (
<div>
<Son info={this.props.info}/>
</div>
);
}
}
class Son extends Component {
render() {
return (
<div>
我是Son組件,拿到信息爲:{this.props.info}
</div>
);
}
}
export default Grandpa;
複製代碼
最後頁面輸出:redux
我是Son組件,拿到信息爲:GrandPa組件的信息
複製代碼
咱們會發現這樣的缺點是一層一層傳值,若是有更多層級和更多數據的話,會讓代碼看起來很不整潔,若是中間哪一個組件忘了傳值基本就完了;並且Father組件也並無用到info值,只是將值傳給Son組件。api
若是使用context,就能幫咱們解決這個層級不停傳值的問題。bash
context有舊版和新版之分,以React v16.3.0版本劃分。react-router
咱們先來講下舊版context APIide
將代碼改爲以下:函數
import React, { Component } from 'react';
import PropTypes from 'prop-types';
class Grandpa extends Component {
state = {
info: 'GrandPa組件的信息'
}
getChildContext () {
return { info: this.state.info }
}
render() {
return (
<div> <Father/> </div>
);
}
}
// 聲明Context對象屬性
Grandpa.childContextTypes = {
info: PropTypes.string
}
class Father extends Component {
render() {
return (
<div> <Son/> </div>
);
}
}
class Son extends Component {
render() {
return (
<div> 我是Son組件,拿到信息爲:{this.context.info} </div>
);
}
}
// 根據約定好的參數類型聲明
Son.contextTypes = {
info: PropTypes.string
}
export default Grandpa;
複製代碼
對PropTypes類型檢查,還能夠寫成下面這種寫法this
class Grandpa extends Component {
static childContextTypes = {
info: PropTypes.string
}
state = {
info: 'GrandPa組件的信息'
}
getChildContext () {
return { info: this.state.info }
}
render() {
return (
<div> <Father/> </div>
);
}
}
複製代碼
在須要傳出去的context值和須要接收context值都要進行類型檢查判斷。
正經常使用這個api可能沒什麼問題,但若是中間哪一個組件用到shouldComponentUpdate
方法的話,就極可能出現問題。
三個組件的層級關係以下
<GrandPa>
<Father>
<Son/>
</Father>
</GrandPa>
複製代碼
若是在GrandPa組件設置按鈕點擊能夠更新info的值,即經過this.setState({info: '改變值'})
方法 更新 Context: 那麼
假如在Father組件添加函數
shouldComponentUpdate () {
return false
}
複製代碼
因爲Father並不依賴任何值,因此咱們默認讓它無需從新render。可是,這會致使Son組件也不會從新render,即沒法獲取到最新的 Context 值。
這樣的不肯定性對於目標組件來講是徹底不可控的,也就是說目標組件沒法保證本身每一次均可以接收到更新後的 Context 值。這是舊版API存在的一個大問題。
而新版API解決了這個問題,咱們來看下新版API怎麼寫的
新版Context 新增了 creactContext() 方法用來建立一個context對象。這個對象包含兩個組件,一個是 Provider(生產者),另外一個是 Consumer(消費者)。
React.createContext 方法用於建立一個 Context 對象。該對象包含 Provider 和 Consumer兩個屬性,分別爲兩個 React 組件。
const InfoContext = React.createContext('');
複製代碼
class Grandpa extends Component {
state = {
info: 'GrandPa組件的信息'
}
render() {
return (
<InfoContext.Provider value={{ info: this.state.info }}> <Father/> </InfoContext.Provider> ); } } 複製代碼
class Son extends Component {
render() {
return (
<InfoContext.Consumer> { value => { return <div>我是Son組件,拿到信息爲:{value.info}</div> } } </InfoContext.Consumer> ); } } 複製代碼