1、为什么要使用 context
没有 context 这个概念之前,我们在组件中自上而下传递数据是通过 props 属性来实现的;如果层级不多用 props 还好,但是如果层级多了,并且只是最后一层会用到,那我们是不是每一层都得写一个 props,这样的话代码写起来很繁琐,不够优雅,不容易维护;所以 context 的出现可以解决这个问题。context 提供了一个无需为每层组件手动添加 props,就能在组件之间进行数据传递的方法。
2、怎么使用 context
React.createContext:创建一个装上下文的容器组件,defaultValue 可以设置需要共享的默认数据;只有在没有被 Provider 容器包裹下的组建使用 context,默认值才会生效;
Context.Provider:提供者,用于提供共享数据的地方,value 属性设置什么数据就共享什么数据;
Context.Consumer:消费者,专门消费 Provider 提供的共享数据,Consumer 需要嵌套在 Provider 下面,才能通过回调的方式拿到共享的数据;(只有函数组件会用到)
Class.contextType:记住是用于指定 contextType 等于当前的 context,必须指定,才能通过 this.context 来访问到共享的数据;(只有类组件会用到)
Context.displayName:context 对象接收一个名为 displayName 的属性,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。
3、使用案例
// theme-context.js
import React from 'react';
export const themes = {
light: {
foreground: '#000',
background: '#eee'
},
dark: {
foreground: '#fff',
background: '#222'
}
}
// Step1: 创建一个装上下文的容器
export const ThemeContext = React.createContext(
themes.dark // defaultValue 默认值
)
// App.js
import React, { Component } from 'react'
import { themes, ThemeContext } from './theme-context.js'
import ToolBar from './component/Toolbar'
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
theme: themes.dark
}
}
toggleTheme = () => {
this.setState(state => ({
theme: state.theme === themes.dark ? themes.light : themes.dark
}))
}
render () {
return (
{/* Step2: 在顶层组件设置需要共享的数据, 到时候在ToolBar里面会用到 */}
<ThemeContext.Provider value={this.state.theme}>
<ToolBar changeTheme={this.toggleTheme}/>
</ThemeContext.Provider>
)
}
}
// ToolBar.js
import React, { Component } from 'react'
import { ThemeContext } from './theme-context.js'
// 1. 如果子组件是类组件,需要指定contextType等于当前的context(也是两种方式)
export default class ToolBar extends Component {
// 方式1:在class组件中声明静态属性static
// static contextType = ThemeContext
render () {
let theme = this.context
return (
<div style={{ border: '1px solid #000', background: theme.background }}>
<h2 style={{ color: theme.foreground }}>ToolBar</h2>
<button onClick={this.props.changeTheme}>
Change Theme
</button>
</div>
)
}
}
// 方式2: 在class组件外面指定
ToolBar.contextType = ThemeContext
// 2. 如果子组件是函数式组件,需要用Consumer组件来包裹,通过value拿到数据
export default function ToolBar (props) {
return (
<ThemeContext.Consumer>
{ theme => (
<div style={{ border: '1px solid #000', background: theme.background }}>
<h2 style={{ color: theme.foreground }}>ToolBar</h2>
<button onClick={props.changeTheme}>
Change Theme
</button>
</div>
)}
</ThemeContext.Consumer>
)
}