寫在前面
鑒於筆者學習此內容章節 React官方文檔 時感到閱讀理解抽象困難,所以決定根據文檔理解寫一篇自己對Context的理解,文章附帶示例,以為更易於理解學習。更多內容請參考 React官方文檔
如果您覺得文章對您有幫助,可以點擊文章右下角【推薦】一下。您的鼓勵是筆者創作的最大動力!
如果發現文章有問題也可以在文章下方及時聯系筆者哦,相互探討一起進步~
基本概念
Context
是React
中為了避免在不同層級組件中逐層傳遞props
的產物,在沒有Context
的時候父組件向子組件傳遞props
屬性只能在組件樹上自上而下進行傳遞,但是有些屬性並不是組件樹的每層節點都有相同的需求,這樣我們再這樣逐層傳遞props
就顯得代碼很繁瑣笨重。- 使用
React.createContext(defulData)
可以通過創建一個ContextObject
,在某個組件中調用ContextObject.Provider
同時可以設置新的value = newData
覆蓋掉defulData
共享到下面的所有子組件,需要ContextObject
共享出來的數據的子組件可以通過static contextType = ContextObject
接收到data
,使用this.context
即可調用data
適用場景
- 很多不同層級的組件需要訪問同樣的數據,所以如果我們只是想避免層層傳遞一些屬性,那么我們還有更好的選擇: 組合組件(合理利用{props.children})
Context API 理解與運用
- React.createContext(defaultValue)
創建一個Context對象,defaultValue
是默認參數,在一個組件中可以調用這個對象的Provider
API,並且設置新的參數:
const Context = React.createContext(defaultValue)
function ContextProvider () {
return (
<Context.Provider value = { newValue }>
/* 子組件(這里的組件及其子組件都可以收到這個Context對象發出的newValue) */
<Context.Provider/>
)
}
但是如果沒有對應的Context.Provider
相匹配,那么組件樹上的所有組件都可以收到這個Context對象發出 的defaultValue
;
同時可以調用Context
的Consumer
API可以用來接受到Context
的值,並且根據這個值渲染組件:
function ContextConsumer () {
return (
<Context.Comsumer>
{value =>
<div>
/* 可以根據value進行渲染 */
</div>
}
</Context.Comsumer>
)
}
-
Context.Provider & Context.Comsumer
-
<MyContext.Provider value={ variableValue }>
可以允許消費組件訂閱到variableValue
值的變化,也就是說消費組件可以根據variableValue
值的變化而變化,variableValue
的值我們可以定義一個事件來控制改變;
<MyContext.Consumer> {value => /* 基於 context 值進行渲染*/} </MyContext.Consumer>
利用
Context.Consumer
API 可以讓我們即使是在函數式組件也可以訂閱到 Context的值;這種方法需要一個函數作為子元素,函數接收當前的context值,並返回一個 React 節點。
傳遞給函數的
value
值等價於組件樹上方離這個 context 最近的 Provider 提供的variableValue
值。如果沒有對應的 Provider,value
參數等同於傳遞給createContext()
的defaultValue
。// Provider 結合 Consumer 使用示例 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // 創建 Context 對象 const MyContext = React.createContext(0) // defaultValue 是數字0 // App組件 渲染 Context 對象 class App extends React.Component { constructor(props){ super(props); this.state = { variableValue : 0 } // 處理 Provider中value變化的函數 this.handleChange = () => { this.setState(state => ({ variableValue: state.variableValue + 1 }) ) } } render(){ return ( // 調用 Context.Provider, 設置可以讓Consumer組件監聽變化的 value 值 <MyContext.Provider value = {this.state.variableValue}> <Context changeValue = {this.handleChange}/> </MyContext.Provider> ) } } // 消費組件 class Context extends React.Component{ render(){ return ( <MyContext.Consumer> /* 根據Context的value進行渲染 */ {value => <button onClick={this.props.changeValue} > Add MyValue:{value} </button> } </MyContext.Consumer> ) } } ReactDOM.render( <App className = 'app'/> , document.getElementById('root') );
當Provider的
variableValue
值發生變化時,它內部的所有消費組件都會重新渲染。 -
-
Class.contextType
class MyClass extends React.Component { render() { let value = this.context; // this.context 可以訪問到 MyClass 的contextType /* 基於 MyContext 組件的值進行渲染 */ } } MyClass.contextType = MyContext; //將MyClass的contextType屬性賦值為 Context 對象的值
掛載在 class 上的
contextType
屬性會被重賦值為一個由React.createContext()
創建的 Context 對象。此屬性能讓你使用this.context
來消費最近 Context 上的那個值。你可以在任何生命周期中訪問到它,包括 render 函數中。注: 從文檔的字面意思,
Class.contextType
是類組件特有的API,所以函數式組件只能使用Context.Consumer
來訪問Context
對象的值,我們可以來試一下類組件和函數式組件的API:import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // 創建 Context 對象 const MyContext = React.createContext(0) // App組件 渲染 Context 對象 class App extends React.Component { constructor(props){ super(props); this.state = { variableValue : 0 } this.handleChange = () => { this.setState(state => ({ variableValue: state.variableValue + 1 }) ) } } render(){ return ( // 調用 Context.Provider, 設置可以讓Consumer組件監聽變化的 value 值 <MyContext.Provider value = {this.state.variableValue}> <Context_consumer changeValue = {this.handleChange} /> <br/> <Context_contextType changeValue = {this.handleChange} /> <br /> <Func_Consumer changeValue = {this.handleChange} /> <br /> <func_contextType changeValue = {this.handleChange} /> </MyContext.Provider> ) } } // Class & Consumer 消費組件 class Context_consumer extends React.Component{ render(){ return ( <MyContext.Consumer> {value => <button onClick={this.props.changeValue} > Add Class_consumer:{value} </button> } </MyContext.Consumer> ) } } // Class & contextType 的消費組件 class Context_contextType extends React.Component{ render(){ let value = this.context return ( <button onClick={this.props.changeValue} > Add Class_contextType:{value} </button> ) } }; Context_contextType.contextType = MyContext; // 函數組件 & Consumer function Func_Consumer (props) { return ( <MyContext.Consumer> {value => <button onClick={props.changeValue} > Add Func_consumer:{value} </button> } </MyContext.Consumer> ) } // 函數組件 & contextType function func_contextType (props) { let value = this.context return ( <button onClick={props.changeValue} > Add func_contextType:{value} </button> ) } func_contextType.contextType = MyContext; ReactDOM.render( <App className = 'app'/> , document.getElementById('root') );
運行結果:
除了func_contextType組件之外其他組件都可以正常運行
-
Context.displayName
context 對象接受一個名為
displayName
的 property,類型為字符串。React DevTools 使用該字符串來確定 context 要顯示的內容。示例,下述組件在 DevTools 中將顯示為 MyDisplayName:
const MyContext = React.createContext(/* some value */); MyContext.displayName = 'MyDisplayName'; <MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中 <MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中