React中Context的API

1. Context

關於context官網文檔有以下的描述:react

  1. If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
  2. If you aren't familiar with state management libraries like Redux or MobX, don't use context.
  3. If you aren't an experienced React developer, don't use context. There is usually a better way to implement functionality just using props and state.

綜上所述就是不要使用context這個API。 雖說不要用,可是咱們也是要了解下這個API究竟是幹嗎的,畢竟有些優秀的庫都是經過這個API實現而來,如:React-Redux。bash

簡單瞭解context的做用就是在某個父組件中定義一個全局狀態,這個狀態能夠在該父組件下的全部子組件中跨級傳遞共享。目前有兩個版本分別是16.x以前和16.x以後的版本。app

2. 老版本的Context

在老版本中有以下幾個方法:ide

getChildContext: 在父組件中聲明一個函數,返回的結果是一個對象,這個對象就是context,能夠對子組件進行共享的狀態。函數

childContextTypes: 在父組件中聲明,執行context中的數據類型,若是不指定會產生錯誤。ui

contextTypes: 在子孫組件中進行聲明,指定要接受context中哪些數據類型。this

Tip: React.PropTypes has moved into a different package since React v15.5. Please use the prop-types library instead to define contextTypes.spa

如上,react15.5已經棄用React.PropTypes,須要安裝prop-types庫。code

看個小例子:component

//父組件
import React from 'react'
import DemoSun from './componets/DemoSun'
import propTyps from 'prop-types'

class Demo extends React.Component {

  getChildContext() {
    return {
      color: 'red'
    }
  }
  render() {
    return (
      <div>
        DEMO
        我是什麼顏色的太陽:<DemoSun />
      </div>
    )
  }
}

Demo.childContextTypes = {
  color: propTyps.string
}

export default Demo 


//子組件
import React from 'react'
import propTyps from 'prop-types'

class DemoSun extends React.Component {

  render() {
    return (
      <div>
        DemoSun
        {this.context.color}
      </div>
    )
  }
}

DemoSun.contextTypes = {
  color: propTyps.string
}

export default DemoSun
複製代碼

結果以下,子組件能夠獲取context中的color的值。

若是 contextTypes定義在某個組件中,則這個組件的生命週期函數中會增長一個參數:

constructor(props, context)
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componentWillUpdate(nextProps, nextState, nextContext)
componentDidUpdate(prevProps, prevState, prevContext)
複製代碼

若是在無狀態組件中使用context則以下:

const PropTypes = require('prop-types');

const Button = ({children}, context) =>
  <button style={{background: context.color}}>
    {children}
  </button>;

Button.contextTypes = {color: PropTypes.string};
複製代碼

關於老版的context就介紹到此,來關注下新版本的context。

3. 新版本的Context

新版本中使用ProviderConsumer模式,在頂層Provider中傳入value,在子孫中的Consumer中獲取該值,而且可以傳遞函數,用來修改context。

  • React.createContext(args):
const Mycontext = React.createContext(defaultValue)
複製代碼

新版的是經過該方法初始化一個context對象。當React渲染了一個訂閱了這個Context對象的組件,這個組件會從組件樹中離自身最近的那個匹配Provider中讀取到當前的context值。只有當組件所處的樹中沒有匹配到Provider時,其defaultValue參數纔會生效。

  • Context.Provider
<Mycontext.Provider value={/*某個值*/}></Mycontext.Provider>
複製代碼

每一個Context對象都會返回一個Provider組件。它容許消費組件訂閱context變化。其有一個value屬性,傳遞給消費組件。一個Provider能夠和多個消費組件有對應關係,多個Provider也能夠嵌套使用,裏層的會覆蓋外層數據。

當Provider的value值發生變化時,它內部的全部消費者組件都會從新渲染。Provider及其內部consumer組件都不受shouldComponentUpdate函數的影響,不管shouldComponentUpdate返回true或者false,所以當consumer組件在其祖先組件退出更新的狀況下也能夠更新。

  • Class.contexType

掛載在class上的contextType靜態屬性會被賦值爲一個由React.createContext()的Context對象。這能讓你使用this.context來消費最近Context上的那個值。你能夠在任何生命週期中訪問它,包括在render中。

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在組件掛載完成後,使用 MyContext 組件的值來執行一些有反作用的操做 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基於 MyContext 組件的值進行渲染 */
  }
}
MyClass.contextType = MyContext;
複製代碼
  • Context.Consumer
<MyContext.Consumer>
    {value=>/*基於context值進行渲染*/}
</MyContext.Consumer>
複製代碼

這裏,React組件也能夠訂閱到context變動。這能讓你在函數式組件中完成訂閱context。Consumer的children必須是一個函數。

這須要函數做爲子元素這種作法。這個函數接受當前的context值,返回一個react節點。傳遞給函數的value值等同於往上組件樹離這個context最近的Provider提供的value值。若是沒有對應的Provider,value參數等於傳遞給createContext()的defaultValue。

4. 注意事項

context會使用參考標識來決定什麼時候進行渲染。這樣就會當provider的父組件進行從新渲染時,可能會在consumer組件中觸發意外的渲染。以下:

class App extends React.Componenet{
    render() {
        return (
            <Provider value={{text: 'text'}}>
                <Demo />
            </Provider>
        )
    }
}
複製代碼

如上,每次value都會建立一個新的對象。爲了不這種狀況,咱們能夠將其提出到state中進行管理。

class App extends React.Componenet{
    constructor(props) {
        super(props)
        this.state = {
            text: 'text'
        }
    }
    render() {
        return (
            <Provider value={{text: this.state.text}}>
                <Demo />
            </Provider>
        )
    }
}
複製代碼
相關文章
相關標籤/搜索