React系列——React Context

前言

咱們先看一下React中,父子組件通訊的機制,父子組件的通訊是經過props進行數據的傳遞:node

一、父組件向子組件傳遞數據(狀態)時,是在調用子組件的時候經過參數傳遞給子組件,子組件經過this.props進行接收;
二、子組件若是更改父組件的一些屬性,則是經過父組件定義的方法來傳遞給子組件,子組件調用更改;
三、若是父組件想要更改子組件的一些狀態時,經過ref進行標記,能夠獲取子組件的全部信息,從而調用子組件的方法和值;

可是,若是層級不少呢,是否須要多個props進行逐層的傳遞?答案是否認的,React的advanced(高級)中指出了context,優雅的解決這個問題。react

初識Context

咱們知道,在JS中context指的是函數的執行上下文,函數被調用時,this指向誰,誰就是當前的執行上下文;redux

react中的context是什麼呢?官方文檔給出:

Context 經過組件樹提供了一個傳遞數據的方法,從而避免了在每個層級手動的傳遞 props 屬性。

文檔也沒具體給出context究竟是什麼,而是告訴咱們context能幹什麼,也就是說,若是咱們不想經過props實現組件樹的逐層傳遞數據,則可使用context實現跨層級進行數據傳遞!api

如何使用Context

context api給出三個概念:React.createContext()、Provider、Consumer;react-router

React.createContext()

這個方法用來建立context對象,幷包含Provider、Consumer兩個組件 <Provider />、<Consumer />

const {Provider, Consumer} = React.createContext();

Provider

數據的生產者,經過value屬性接收存儲的公共狀態,來傳遞給子組件或後代組件

eg:

<Provider value={/* some value */}>

Consumer

數據的消費者,經過訂閱Provider傳入的context的值,來實時更新當前組件的狀態

eg: 

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>
值得一提的是每當Provider的值發生改變時, 做爲Provider後代的全部Consumers都會從新渲染

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子組件的數據傳遞;

解析React-redux中的context

1) 衆所周知,react-redux是連接react、redux的橋樑,咱們來解剖一下到底react-redux是怎麼搭這座橋樑的;

用redux來實現數據的傳遞:

圖片描述

那麼,使用Redux不是不能夠,只不過用起來着實麻煩,須要一直訂閱store裏的數據,每次都須要寫一遍,爲了方便,Redux的做者封裝了React專用的庫react-redux;

圖片描述

可見,react-redux其實是經過Provider組件和connect方法進行鏈接react和redux,那麼他們到底本質上就是經過Context來傳遞數據

圖片描述

React-router中的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已經很好用了,可是官方仍是沒有大面積的去推廣,可能有一些其餘的問題,好比多個context的管理,可是咱們仍是很看好,他的將來,畢竟react-redux、react-router還在用

相關文章
相關標籤/搜索