咱們先看一下React中,父子組件通訊的機制,父子組件的通訊是經過props進行數據的傳遞:node
一、父組件向子組件傳遞數據(狀態)時,是在調用子組件的時候經過參數傳遞給子組件,子組件經過this.props進行接收;
二、子組件若是更改父組件的一些屬性,則是經過父組件定義的方法來傳遞給子組件,子組件調用更改;
三、若是父組件想要更改子組件的一些狀態時,經過ref進行標記,能夠獲取子組件的全部信息,從而調用子組件的方法和值;
可是,若是層級不少呢,是否須要多個props進行逐層的傳遞?答案是否認的,React的advanced(高級)中指出了context,優雅的解決這個問題。react
咱們知道,在JS中context指的是函數的執行上下文,函數被調用時,this指向誰,誰就是當前的執行上下文;redux
Context 經過組件樹提供了一個傳遞數據的方法,從而避免了在每個層級手動的傳遞 props 屬性。
文檔也沒具體給出context究竟是什麼,而是告訴咱們context能幹什麼,也就是說,若是咱們不想經過props實現組件樹的逐層傳遞數據,則可使用context實現跨層級進行數據傳遞!api
context api給出三個概念:React.createContext()、Provider、Consumer;react-router
這個方法用來建立context對象,幷包含Provider、Consumer兩個組件 <Provider />、<Consumer /> const {Provider, Consumer} = React.createContext();
數據的生產者,經過value屬性接收存儲的公共狀態,來傳遞給子組件或後代組件 eg: <Provider value={/* some value */}>
數據的消費者,經過訂閱Provider傳入的context的值,來實時更新當前組件的狀態 eg: <Consumer> {value => /* render something based on the context value */} </Consumer>
props單向數據流動:dom
若是以爲Props傳遞數據很繁瑣,能夠採用context,進行跨組件傳遞數據ide
再最外層的組件上,經過生產者Provider組件進行包裹,並存儲共享數據到value中,固然能夠是任何數據類型。後帶須要用到共享數據的組件都可經過Consumer進行數據獲取。函數
新版的context是即react16.3之後進行升級的,可是16.3之前的context卻不是那麼好用了,同時它還被賦予「實驗性」帽子的稱號。ui
舊版本被賦予生產者和消費者的概念:this
對於父組件來講,也就是生產者,須要聲明childContextTypes對象,來提供給子組件須要用到的公共數據;
其次,還須要實例化getChildContext方法,返回一個純對象,必定要注意的是父組件在getChildContext方法中定義數據的時候,必定要先在childContextTypes中進行聲明,不然會報錯;
用代碼描述是:
父組件: // 父組件聲明context數據 childContextTypes = { orderVal: PropTypes.string, changeVal: PropTypes.func }; // 實例化getChildContext方法,返回一個純對象 getChildContext() { return { orderVal: this.state.orderVal } }; 子組件: // 子組件則聲明須要使用的Context屬性 static contextTypes = { orderVal: PropTypes.string }; // 經過this.context獲取父組件的屬性 render(){ const { orderVal } = this.context; return ( <div>{orderVal}</div> ) }
若是某個組件更改了父組件的某個屬性值,那麼父組件將會從新render組件樹,若是期間有一個子組件沒有用到context中的這個屬性,並定義了shouldComponentUpdate,那和這個子組件的子孫組件就不會從新render,這樣就會很危險,而新版本的context api卻彌補了這個問題,即便其中的某個組件沒有數據更新,也會影響到子孫組件,就是經過一個Provider來支持多個Consumer子組件的數據傳遞;
1) 衆所周知,react-redux是連接react、redux的橋樑,咱們來解剖一下到底react-redux是怎麼搭這座橋樑的;
用redux來實現數據的傳遞:
那麼,使用Redux不是不能夠,只不過用起來着實麻煩,須要一直訂閱store裏的數據,每次都須要寫一遍,爲了方便,Redux的做者封裝了React專用的庫react-redux;
可見,react-redux其實是經過Provider組件和connect方法進行鏈接react和redux,那麼他們到底本質上就是經過Context來傳遞數據
一樣,咱們來看一下React-router中是怎麼用context的
看一下Router組件的部分代碼片斷,react-router-dom用的仍是react-router3版本的Router組件,其中的context採用也是舊版的寫法,之後可能會升級!
var Router = function (_React$Component) { ... // Router組件擴展一個getChildContext方法,爲子組件提供一個帶有router屬性的context // 監聽history,history發生變化,並從新對route進行賦值 Router.prototype.getChildContext = function getChildContext() { return { router: _extends({}, this.context.router, { history: this.props.history, route: { location: this.props.history.location, match: this.state.match } }) }; }; ... Router.prototype.componentWillReceiveProps = function componentWillReceiveProps(nextProps) { warning(this.props.history === nextProps.history, "You cannot change <Router history>"); }; ... Router.prototype.render = function render() { var children = this.props.children; return children ? React.Children.only(children) : null; }; return Router; }(React.Component); Router.propTypes = { history: PropTypes.object.isRequired, children: PropTypes.node }; Router.contextTypes = { router: PropTypes.object }; Router.childContextTypes = { router: PropTypes.object.isRequired }; export default Router;
一樣,Route組件、Link組件等,都是經過Context來構建的,經過context來共享router,並匹配當前路由的路徑,來動態加載子組件;
可是,路由只會給子組件共享context,那若是是子孫組件,怎麼辦呢?也給他包一層router,這樣不也就能共享context了嗎?那麼咱們怎麼在包一層呢?
答案: withRouter
withRouter用來修飾的這個UI組件,會在外層添加一層Route,這樣就會共享context的狀態了,因此不管咱們在什麼地方來進行路由切換,都須要包一層withRouter的緣由了!
雖然說新版的context已經很好用了,可是官方仍是沒有大面積的去推廣,可能有一些其餘的問題,好比多個context的管理,可是咱們仍是很看好,他的將來,畢竟react-redux、react-router還在用