Context 提供了一個無需為每層組件手動添加 props,就能在組件樹間進行數據傳遞的方法。
1. 用法
React.createContext
const MyContext = React.createContext(defaultValue);
創建一個 Context 對象。當 React 渲染一個訂閱了這個 Context 對象的組件,這個組件會從組件樹中離自身最近的那個匹配的 Provider 中讀取到當前的 context 值。
只有當組件所處的樹中沒有匹配到 Provider 時,其 defaultValue 參數才會生效。這有助於在不使用 Provider 包裝組件的情況下對組件進行測試。注意:將 undefined 傳遞給 Provider 的 value 時,消費組件的 defaultValue 不會生效。
Context.Provider
<MyContext.Provider value={/* 某個值 */}>
每個 Context 對象都會返回一個 Provider React 組件,它允許消費組件訂閱 context 的變化。
Provider 接收一個 value 屬性,傳遞給消費組件。一個 Provider 可以和多個消費組件有對應關系。多個 Provider 也可以嵌套使用,里層的會覆蓋外層的數據。
Context.Consumer
<MyContext.Consumer>
{value => /* 基於 context 值進行渲染*/}
</MyContext.Consumer>
這里,React 組件也可以訂閱到 context 變更。這能讓你在函數式組件中完成訂閱 context。
2. 實例應用
如圖,在父組件中定義兩個變量:fruit 和 count。子組件可以拿到父組件的fruit,子子組件可以拿到父組件的 fruit,並且可以改變count的值。
實現步驟如下:
2.1 新建文件 context.js
創建一個Context 對象,並導出Provider和Consumer容器。
import { createContext } from 'react'
export const { Provider, Consumer } = createContext()
2.2 新建父組件 index.js
import React, { Component } from 'react'
import { Provider } from './context' // 引入Provider
import Son from './Son' // 引入子組件
class Main extends Component {
constructor(props) {
super(props)
this.state = {
fruit: 'apple',
count: 0,
}
}
componentDidMount() {}
getContext = () => {
const { fruit, count } = this.state
return {
fruit,
countUtil: {
addCount: num => {
this.setState({
count: count + num,
})
},
delCount: num => {
this.setState({
count: count - num,
})
},
},
}
}
render() {
const { fruit, count } = this.state
return (
// Provider 容器, 其value接收一個getContext方法
<Provider value={this.getContext()}>
父組件 fruit = {fruit}, count = {count}
<hr />
<Son />
</Provider>
)
}
}
export default Main
2.3 新建子組件 Son.js
import React, { Component } from 'react'
import { Consumer } from './context' // 引入Consumer
import GrandSon from './GrandSon' // 引入子子組件
class Main extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
// Consumer 容器,可以拿到父組件傳遞下來的 fruit 屬性, 並可以展示對應的值
<Consumer>
{context => (
<div>
子組件 fruit={context.fruit}
<hr />
<GrandSon />
</div>
)}
</Consumer>
)
}
}
export default Main
2.4 新建子子組件 GrandSon.js
import React, { Component } from 'react'
import { Consumer } from './context' // 引入Consumer
class Main extends Component {
constructor(props) {
super(props)
this.state = {}
}
render() {
return (
// Consumer 容器,可以拿到父組件傳遞下來的 fruit 屬性, 以及 countUtil對象下的 addCount 和 delCount 方法
<Consumer>
{context => (
<div>
子子組件 fruit={context.fruit}
<br />
<button type="button" onClick={() => context.countUtil.addCount(2)}>
加2
</button>
<button type="button" onClick={() => context.countUtil.delCount(3)}>
減3
</button>
<hr />
</div>
)}
</Consumer>
)
}
}
export default Main