關於context官網文檔有以下的描述:react
- 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.
- If you aren't familiar with state management libraries like Redux or MobX, don't use context.
- 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
在老版本中有以下幾個方法: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的值。
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。
新版本中使用Provider和Consumer模式,在頂層Provider中傳入value,在子孫中的Consumer中獲取該值,而且可以傳遞函數,用來修改context。
const Mycontext = React.createContext(defaultValue)
複製代碼
新版的是經過該方法初始化一個context對象。當React渲染了一個訂閱了這個Context對象的組件,這個組件會從組件樹中離自身最近的那個匹配Provider中讀取到當前的context值。只有當組件所處的樹中沒有匹配到Provider時,其defaultValue參數纔會生效。
<Mycontext.Provider value={/*某個值*/}></Mycontext.Provider>
複製代碼
每一個Context對象都會返回一個Provider組件。它容許消費組件訂閱context變化。其有一個value屬性,傳遞給消費組件。一個Provider能夠和多個消費組件有對應關係,多個Provider也能夠嵌套使用,裏層的會覆蓋外層數據。
當Provider的value值發生變化時,它內部的全部消費者組件都會從新渲染。Provider及其內部consumer組件都不受shouldComponentUpdate函數的影響,不管shouldComponentUpdate返回true或者false,所以當consumer組件在其祖先組件退出更新的狀況下也能夠更新。
掛載在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;
複製代碼
<MyContext.Consumer>
{value=>/*基於context值進行渲染*/}
</MyContext.Consumer>
複製代碼
這裏,React組件也能夠訂閱到context變動。這能讓你在函數式組件中完成訂閱context。Consumer的children必須是一個函數。
這須要函數做爲子元素這種作法。這個函數接受當前的context值,返回一個react節點。傳遞給函數的value值等同於往上組件樹離這個context最近的Provider提供的value值。若是沒有對應的Provider,value參數等於傳遞給createContext()的defaultValue。
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>
)
}
}
複製代碼