參考資料:javascript
這是牛津詞典裏有關 context 的解釋。一般咱們在編程當中會把 context 叫作 上下文,不過我認爲這裏彷佛叫作 語境更合適一些。可是不論是叫作 上下文或是 語境,都仍是有些抽象。html
在學生時代,語文試卷中都會有個閱讀理解部分,一般會有個問題相似於「結合上下文,x x x x x x x」,例如:java
..... 林沖大叫一聲「啊也!」 ..... 問:這句話林沖的「啊也」表達了林沖怎樣的內心? 答:啊你媽個頭啊!react
看,一篇文章,給你摘錄一段,沒前沒後,你讀不懂。由於有 語境,就是語言環境存在,一段話說了什麼,要經過上下文(文章的上下文)來推斷。git
那麼從編程的角度又該如何理解呢?一般咱們寫代碼的時候,除了一些簡單的函數不須要外部變量,其餘的複雜些的函數大多會依賴一些外部變量。而一旦須要外部變量,那麼這段代碼就不是完整的,不是可以獨立運行的,而這裏這些程序運行所必需的外部變量(也能夠理解爲前置條件),就叫作 上下文。github
首先,react 在 16.3 引入了context,通過不過的迭代,到如今16.8.6的版本中, 官方對context的定義爲 --- Context 提供了一個無需爲每層組件手動添加 props,就能在組件樹間進行數據傳遞的方法。歸類於高級指引部分,屬於react的高級api。web
一般在咱們寫react組件的時候,數據傳遞通常採用單相數據流的方式,這樣可使數據流向變得的簡單而清晰。但在某些使用場景中須要共享一些對於一個組件樹而言是**「全局」**的數據,這時就須要在每個層級手動修改傳輸的props,過於麻煩。 例如官方文檔中提供的demo:編程
class App extends React.Component {
render() {
return <Toolbar theme="dark" />;
}
}
function Toolbar(props) {
// Toolbar 組件接受一個額外的「theme」屬性,而後傳遞給 ThemedButton 組件。
// 若是應用中每個單獨的按鈕都須要知道 theme 的值,這會是件很麻煩的事,
// 由於必須將這個值層層傳遞全部組件。
return (
<div>
<ThemedButton theme={props.theme} />
</div>
);
}
class ThemedButton extends React.Component {
render() {
return <Button theme={this.props.theme} />;
}
}
複製代碼
在上述代碼中,修改Button的theme,那麼須要從App中修改theme,傳給Toolbar,並由Toolbar傳給ThemedButton使用,如此層層傳遞。若是ThemedButton與App以前還存在更多的嵌套關係,那麼必需將theme在中間的各層級間層層傳遞,顯然,這樣過於繁瑣了。redux
// Context 可讓咱們無須明確地傳遍每個組件,就能將值深刻傳遞進組件樹。
// 爲當前的 theme 建立一個 context(「light」爲默認值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
render() {
// 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。
// 不管多深,任何組件都能讀取這個值。
// 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
return (
<ThemeContext.Provider value="dark">
<Toolbar />
</ThemeContext.Provider>
);
}
}
// 中間的組件不再必指明往下傳遞 theme 了。
function Toolbar(props) {
return (
<div>
<ThemedButton />
</div>
);
}
class ThemedButton extends React.Component {
// 指定 contextType 讀取當前的 theme context。
// React 會往上找到最近的 theme Provider,而後使用它的值。
// 在這個例子中,當前的 theme 值爲 「dark」。
static contextType = ThemeContext;
render() {
return <Button theme={this.context} />;
}
}
複製代碼
這是16.8.6版本中的文檔demo(本文不對16.x以前的context使用方式作探討,有興趣同窗可自行研究)api
const ThemeContext = React.createContext('light');
複製代碼
class App extends React.Component {
render() {
// 使用一個 Provider 來將當前的 theme 傳遞給如下的組件樹。
// 不管多深,任何組件都能讀取這個值。
// 在這個例子中,咱們將 「dark」 做爲當前的值傳遞下去。
return (
<ThemeContext.Provider value="dark"> <Toolbar /> </ThemeContext.Provider> ); } } 複製代碼
class ThemedButton extends React.Component {
// 指定 contextType 讀取當前的 theme context。
// React 會往上找到最近的 theme Provider,而後使用它的值。
// 在這個例子中,當前的 theme 值爲 「dark」。
static contextType = ThemeContext;
render() {
// 掛載在 class 上的 contextType 屬性會被重賦值爲一個由 React.createContext() 建立的 Context 對象。這能讓你使用 this.context 來消費最近 Context 上的那個值。
return <Button theme={this.context} />; } } 複製代碼
4.中間無需使用theme的組件,不須要層層傳遞props
// 中間的組件不再必指明往下傳遞 theme 了。
function Toolbar(props) {
return (
<div> <ThemedButton /> </div>
);
}
複製代碼
class ThemedButton extends React.Component {
// 指定 contextType 讀取當前的 theme context。
// React 會往上找到最近的 theme Provider,而後使用它的值。
// 在這個例子中,當前的 theme 值爲 「dark」。
static contextType = ThemeContext;
render() {
return (
<ThemeContext.Consumer>
//Consumer容器,能夠拿到上文傳遞下來的 theme 屬性,並能夠展現對應的值
{(theme) =>
<Button theme={theme} />
}
</ThemeContext.Consumer>
);
}
}
複製代碼
1.由於每次Context值變動時,Consumer都會接受到相應的變化通知,這裏可能會有一些陷阱,當 Provider 的父組件進行重渲染時,可能會在 Consumers 組件中觸發意外的渲染。例如
class App extends React.Component {
render() {
//當每一次 Provider 重渲染時(即每次render時{something: 'something'}都指向一個新對象,拓展1),如下的代碼會重渲染全部下面的 consumers 組件,由於 value 屬性老是被賦值爲新的對象
return (
<Provider value={{something: 'something'}}> <Toolbar /> </Provider>
);
}
}
複製代碼
爲了防止這種狀況,將 value 狀態提高到父節點的 state 裏:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
value: {something: 'something'},
};
}
render() {
return (
<Provider value={this.state.value}> <Toolbar /> </Provider>
);
}
}
複製代碼
2.組件的複用性下降
1.引用類型 2.mobx-react / react-router / react-redux 均適用context,有興趣可自行探究。