Context提供了一种方式,能够让数据在组件树种传递而不必使用
Props
一级一级手动传递
系列目录
- React新特性-Context(一)
- React新特性-Lazy和Suspense(二)
- React新特性-memo(三)
- React新特性-Hooks之useState(四)
- React新特性-Hooks之useEffect和useLayoutEffect(五)
- React新特性-Hooks之useContext(六)
- React新特性-Hooks之useRef(七)
- React新特性-Hooks之useMemo和useCallback(八)
- React新特性-Hooks之useReducer(九)
- React新特性-Hooks之自定义Hook(十完结)
何时使用 Context
例如:
export default class Page extends Component {
constructor(props){
super(props)
this.state={
theme:'red'
}
}
onChangeTheme =(color)=>{
this.setState({theme:color})
}
render() {
const data = [{
id: 1,
text: '随随便便输入',
color: 'yellow'
}, {
id: 2,
text: '随便输入',
color: 'blue'
}]
return (
<div>
<List data={data} theme={this.state.theme} onChangeTheme={this.onChangeTheme} />
</div>
)
}
}
class List extends Component {
// List 组件接受一个额外的“theme,onChangeTheme”属性,然后传递给 Item 组件。
// 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
// 因为必须将这个值层层传递所有组件。
render() {
return (
<div>
{
this.props.data.map(i => (
<Item
key={i.key}
theme={this.props.theme}
onChangeTheme={this.props.onChangeTheme}
color={i.color}>
{i.text}
</Item>
))
}
</div>
)
}
}
class Item extends Component {
render() {
return (
<div>
<p style={{ color: this.props.theme }}>
{this.props.children} <button onClick={() => this.props.onChangeTheme(this.props.color)}>点击变色</button>
</p>
</div>
)
}
}
上面的例子theme
不仅仅层层传递,可能中间层List根本就用不到theme
,但是每次还是要获取theme
进行传递下去

下面来看看使用Context如何解决theme
属性和onChangeTheme
事件层层传递的问题
// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
const ThemeContext = React.createContext()
export default class Page extends Component {
constructor(props){
super(props)
this.state={
theme:'red'
}
}
onChangeTheme =(color)=>{
this.setState({theme:color})
}
render() {
const data = [{
id: 1,
text: '随随便便输入',
color: 'yellow'
}, {
id: 2,
text: '随便输入',
color: 'blue'
}]
// 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
// 无论多深,任何组件都能读取这个value值。
return (
<div>
<ThemeContext.Provider value={{
theme:this.state.theme,
onChangeTheme:this.onChangeTheme
}}>
<List data={data} />
</ThemeContext.Provider >
</div>
)
}
}
//这里就不需要通过props传递theme和onChangeTheme
class List extends Component {
render() {
return (
<div>
{this.props.data.map(i => (<Item key={i.id} color={i.color}>{i.text}</Item>))}
</div>
)
}
}
class Item extends Component {
// 指定 contextType 读取当前的 theme context。
// React 会往上找到最近的 theme Provider,然后使用它的值。
static contextType = ThemeContext;
render() {
return (
<div>
<p style={{ color: this.context.theme }}>
{this.props.children}
<button onClick={() => this.context.onChangeTheme(this.props.color)}>
点击变色
</button>
</p>
<ThemeContext.Consumer>
{
value=>{
console.log(value,'render')
}
}
</ThemeContext.Consumer>
</div>
)
}
}

Context API
React.createContext
const MyContext = React.createContext(defaultValue);
创建一个Context实例对象,同时这个对象派生出两个React组件,一个是<Context.Provider>
,另一个是<Context.Consumer>
。
Context.Provider(生产者组件)
<MyContext.Provider value={/* 某个值 */}>
Provider
接收一个 value
属性,传递给所有子级组件(消费者组件)。一个 Provider
可以和多个子集组件有对应关系。多个 Provider
也可以嵌套使用,里层的会覆盖外层的数据。
当 Provider
的 value
值发生变化时,它内部的所有子集组件都会重新渲染。
Context.Consumer(消费者组件)
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
消费者组件,用来接收Provider
提供的变量value
值,这里Consumer
不能直接拿到value
值,必须要以一个函数来拿出value
值
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 组件的值进行渲染 */
}
}
//挂载到class身上
MyClass.contextType = MyContext;
挂载在 class
上的 contextType
属性会被重赋值为一个由 React.createContext()
创建的 Context
对象。这能让你使用 this.context
来消费最近 Context
上的那个值。你可以在任何生命周期中访问到它,包括 render
函数中。
总结
使用React.createContext
创建Context
对象,然后利用<Context.Provider>
组件包裹子级组件传递给所有子级组件,子集组件不能直接拿到Provider
中的value
值,所以使用<Context.Consumer>
或者contextType
这两种方式去拿到Provider
中的value
值
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。