React.js 的 context

這一節咱們來介紹一個你可能永遠用不上的 React.js 特性 —— context。可是瞭解它對於瞭解接下來要講解的 React-redux 頗有好處,因此你們能夠簡單瞭解一下它的概念和做用。html

在過去很長一段時間裏面,React.js 的 context 一直被視爲一個不穩定的、危險的、可能會被去掉的特性而不被官網文檔所記載。可是全世界的第三方庫都在用使用這個特性,直到了 React.js 的 v0.14.1 版本,context 才被官方文檔所記錄。前端

除非你以爲本身的 React.js 水平已經比較爐火純青了,不然你永遠不要使用 context。就像你學 JavaScript 的時候,老是會被提醒不要用全局變量同樣,React.js 的 context 其實像就是組件樹上某顆子樹的全局變量。redux

想象一下咱們有這麼一棵組件樹:函數

假設如今這個組件樹表明的應用是用戶能夠自主換主題色的,每一個子組件會根據主題色的不一樣調整本身的字體顏色或者背景顏色。「主題色」這個玩意是全部組件共享的狀態,根據咱們在 前端應用狀態管理 —— 狀態提高 中所提到的,須要把這個狀態提高到根節點的 Index 上,而後把這個狀態經過 props 一層層傳遞下去:字體

假設原來主題色是綠色,那麼 Index 上保存的就是 this.state = { themeColor: 'green' }。若是要改變主題色,能夠直接經過 this.setState({ themeColor: 'red' })來進行。這樣整顆組件樹就會從新渲染,子組件也就能夠根據從新傳進來的 props.themeColor 來調整本身的顏色。this

但這裏的問題也是很是明顯的,咱們須要把 themeColor 這個狀態一層層手動地從組件樹頂層往下傳,每層都須要寫 props.themeColor。若是咱們的組件樹很層次很深的話,這樣維護起來簡直是災難。spa

若是這顆組件樹可以全局共享這個狀態就行了,咱們要的時候就去取這個狀態,不用手動地傳:code

就像這樣,Index 把 state.themeColor 放到某個地方,這個地方是每一個 Index 的子組件均可以訪問到的。當某個子組件須要的時候就直接去那個地方拿就行了,而不須要一層層地經過 props 來獲取。無論組件樹的層次有多深,任何一個組件均可以直接到這個公共的地方提取 themeColor 狀態。component

React.js 的 context 就是這麼一個東西,某個組件只要往本身的 context 裏面放了某些狀態,這個組件之下的全部子組件都直接訪問這個狀態而不須要經過中間組件的傳遞。一個組件的 context 只有它的子組件可以訪問,它的父組件是不能訪問到的,你能夠理解每一個組件的 context 就是瀑布的源頭,只能往下流不能往上飛。htm

咱們看看 React.js 的 context 代碼怎麼寫,咱們先把總體的組件樹搭建起來,這裏不涉及到 context 相關的內容:

class Index extends Component {
  render () {
    return (
      <div>
        <Header />
        <Main />
      </div>
    )
  }
}

class Header extends Component {
  render () {
    return (
    <div>
      <h2>This is header</h2>
      <Title />
    </div>
    )
  }
}

class Main extends Component {
  render () {
    return (
    <div>
      <h2>This is main</h2>
      <Content />
    </div>
    )
  }
}

class Title extends Component {
  render () {
    return (
      <h1>React.js 小書標題</h1>
    )
  }
}

class Content extends Component {
  render () {
    return (
    <div>
      <h2>React.js 小書內容</h2>
    </div>
    )
  }
}

ReactDOM.render(
  <Index />,
  document.getElementById('root')
)

代碼很長可是很簡單,這裏就不解釋了。

如今咱們修改 Index,讓它往本身的 context 裏面放一個 themeColor

class Index extends Component {
  static childContextTypes = {
    themeColor: PropTypes.string
  }

  constructor () {
    super()
    this.state = { themeColor: 'red' }
  }

  getChildContext () {
    return { themeColor: this.state.themeColor }
  }

  render () {
    return (
      <div>
        <Header />
        <Main />
      </div>
    )
  }
}

構造函數裏面的內容其實就很好理解,就是往 state 裏面初始化一個 themeColor 狀態。getChildContext 這個方法就是設置 context 的過程,它返回的對象就是 context(也就是上圖中處於中間的方塊),全部的子組件均可以訪問到這個對象。咱們用 this.state.themeColor 來設置了 context 裏面的 themeColor

還有一個看起來很可怕的 childContextTypes,它的做用其實 propsType 驗證組件 props 參數的做用相似。不過它是驗證 getChildContext 返回的對象。爲何要驗證 context,由於 context 是一個危險的特性,按照 React.js 團隊的想法就是,把危險的事情搞複雜一些,提升使用門檻人們就不會去用了。若是你要給組件設置 context,那麼 childContextTypes 是必寫的。

如今咱們已經完成了 Index 往 context 裏面放置狀態的工做了,接下來咱們要看看子組件怎麼獲取這個狀態,修改 Index 的孫子組件 Title

class Title extends Component {
  static contextTypes = {
    themeColor: PropTypes.string
  }

  render () {
    return (
      <h1 style={{ color: this.context.themeColor }}>React.js 小書標題</h1>
    )
  }
}

子組件要獲取 context 裏面的內容的話,就必須寫 contextTypes 來聲明和驗證你須要獲取的狀態的類型,它也是必寫的,若是你不寫就沒法獲取 context 裏面的狀態。Title 想獲取 themeColor,它是一個字符串,咱們就在 contextTypes 裏面進行聲明。

聲明之後咱們就能夠經過 this.context.themeColor 獲取到在 Index 放置的值爲 red 的 themeColor,而後設置 h1 的樣式,因此你會看到頁面上的字體是紅色的:

若是咱們要改顏色,只須要在 Index 裏面 setState 就能夠了,子組件會從新渲染,渲染的時候會從新取 context 的內容,例如咱們給 Index 調整一下顏色:

...
  componentWillMount () {
    this.setState({ themeColor: 'green' })
  }
...

那麼 Title 裏面的字體就會顯示綠色。咱們能夠如法炮製孫子組件 Content,或者任意的 Index 下面的子組件。讓它們能夠不通過中間 props 的傳遞獲就能夠獲取到由 Index 設定的 context 內容。

總結

一個組件能夠經過 getChildContext 方法返回一個對象,這個對象就是子樹的 context,提供 context 的組件必須提供 childContextTypes 做爲 context 的聲明和驗證。

若是一個組件設置了 context,那麼它的子組件均可以直接訪問到裏面的內容,它就像這個組件爲根的子樹的全局變量。任意深度的子組件均可以經過 contextTypes 來聲明你想要的 context 裏面的哪些狀態,而後能夠經過 this.context 訪問到那些狀態。

context 打破了組件和組件之間經過 props 傳遞數據的規範,極大地加強了組件之間的耦合性。並且,就如全局變量同樣,context 裏面的數據能被隨意接觸就能被隨意修改,每一個組件都可以改 context 裏面的內容會致使程序的運行不可預料。

可是這種機制對於前端應用狀態管理來講是頗有幫助的,由於畢竟不少狀態都會在組件之間進行共享,context 會給咱們帶來很大的方便。一些第三方的前端應用狀態管理的庫(例如 Redux)就是充分地利用了這種機制給咱們提供便利的狀態管理服務。但咱們通常不須要手動寫 context,也不要用它,只須要用好這些第三方的應用狀態管理庫就好了。

相關文章
相關標籤/搜索