前言
Context 提供了一个无需未每层组件手动添加 props,就能在组件树之间进行数据传递的方法。
在一个典型的 React 应用中,数据是通过 props 属性自上而下(从父组件到子组件)进行传递的,但是这样的方式对于某些类型的属性显得十分繁琐(例如地区偏好,UI 主题),这些属性是应用程序中许多组件都需要的,Context 提供了一个在组件之间共享此类值的方式,而不必显式地通过组件树逐层传递 props 。
场景
Context 设计的目的是为了共享那些对于组件树而言是全局的数据,例如当前认证的用户、主题或者语言。
下面的例子,通过一个 theme 属性手动调整一个按钮组件的样式:
class App extends React.component {
render(){
return <Toolbar theme='dark'></Toolbar>;
}
}
// Toolbar 组件接受一个额外的 theme 属性,传递给 ThemeButton 组件
// 如果应用中每一个单独的组件都需要获得 theme 的值,就需要层层传递到所有组件
function Toolbar(props){
return (
<div>
<ThemeButton theme={props.theme} ></ThemeButton>
</div>
)
}
function ThemeButton(props){
return <Button theme={props.theme}></Button>
}
使用 Context 就可以避免层层传递:
// 为 theme 创建一个 context
const ThemeContext = React.createContext('light');
class App extends React.Component{
render(){
// 使用一个 provider 将当前的 theme 传递给下面的组件树
return (
<ThemeContext.Provider value='dark'>
<Toolbar></Toolbar>
</ThemeContext.Provider>
);
}
}
// 中间组件不需要传递 theme
function Toolbar(){
return (
<div>
<ThemeButton></ThemeButton>
</div>
);
}
class ThemeButton extends React.Component{
// React 会从当前组件顺着组件树往上找到最近的 Provider 使用它的值,没找到就使用默认值
static contextType = ThemeContext;
render(){
return <Button theme={this.context}></Button>;
}
}
使用方法
创建 Context
使用 React.createContext 来创建,生成的 Context 对象的数据结构:
- 两个 currentValue (用来支持多个渲染器)
- Provider
- 包含指向当前 Context 的引用
- Consumer
- 包含指向当前 Context 的引用
- 当前组件向上没有匹配到 Provider 的时候使用 createContext 的默认值(用来测试)
声明 Context 作用域
使用 Context.Provider 来声明使用 Context 的作用域,对应的节点会使用 updateContextProvider 来使用 value 更新 Context
一般都是通过修改 Provider 的 value 的状态来更新 Context
组件中使用
React 提供三种方法来使用 Context 分别对应不同的场景:
- Consumer 用于 JSX
- contextType 用于 class 组件
- 多个 context 好像没法使用该方法
- useContext 用于函数组件
内部都会调用 readContext 来获取最近 Provider 提供的 context 最新值
使用 context 的组件会随着 context 修改而重新渲染