Context
Context
提供了一個無需爲每層組件手動添加 props
,就能在組件樹間進行數據傳遞的方法。
一般情況下,數據是通過 props
屬性自上而下進行傳遞的,但這種做法對於某些類型的屬性是極其繁瑣的,比如主題,這些屬性是應用程序中許多組件都需要的。Context
提供了一種在組件之間共享此類值的方式,而不必顯示地通過組件樹逐層傳遞 props
。
Context
設計目的是爲了共享那些對於一個組件樹而言是 “全局” 的數據,比如前面提到的主題。在下面代碼中通過一個 theme
屬性手動調整一個按鈕組件的樣式:
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} />;
}
}
使用 context
,就可以避免通過中間元素傳遞 props
:
// 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} />;
}
}
Context
主要應用場景在於很多不同層級的組件需要訪問同樣的一些數據。如果只是想避免層層傳遞一些屬性,組件組合會比 context
更好。
相關的API
React.createContext
const MyContext = React.createContext(defaultValue);
創建一個 Context
對象,當 React
渲染一個訂閱了這個 Context
對象的組件,這個組件會從組件樹中離自身最近的那個匹配的 Provider
中讀取到當前的 context
值。只有當組件所處的樹中沒有匹配到 Provider
時,其 defaultValue
參數纔會生效。
Context.Provider
<MyContext.Provider value={/* 某個值 */}>
每個 Context
對象都會返回一個 Provider React
組件,它允許消費組件訂閱 context
的變化。Provider
接收一個 value
屬性,傳遞給消費組件,一個 Provider
可以和多個消費組件有對應關係。多個 Provider
也可以嵌套使用,裏面的會覆蓋外層的數據。
當 Provider
的 value
值發生變化時,它內部的所有消費組件都會重新渲染。Provider
及其內部的 consumer
組件都不受制於 shouldComponentUpdate
,因此當 consumer
組件在其祖先退出更新的情況下也能更新。
需要注意的是,新舊值的變化通過 Object.is
一樣的算法來完成的。
Class.contextType
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;
掛載在 class
上的 contextType
屬性會被重賦值爲一個由 React.createContext()
創建的 Context
對象。這能讓我們使用 this.context
來消費最近 Context
上的那個值。