写在前面
鉴于笔者学习此内容章节 React官方文档 时感到阅读理解抽象困难,所以决定根据文档理解写一篇自己对Context的理解,文章附带示例,以为更易于理解学习。更多内容请参考 React官方文档
如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是笔者创作的最大动力!
如果发现文章有问题也可以在文章下方及时联系笔者哦,相互探讨一起进步~
基本概念
Context
是React
中为了避免在不同层级组件中逐层传递props
的产物,在没有Context
的时候父组件向子组件传递props
属性只能在组件树上自上而下进行传递,但是有些属性并不是组件树的每层节点都有相同的需求,这样我们再这样逐层传递props
就显得代码很繁琐笨重。- 使用
React.createContext(defulData)
可以通过创建一个ContextObject
,在某个组件中调用ContextObject.Provider
同时可以设置新的value = newData
覆盖掉defulData
共享到下面的所有子组件,需要ContextObject
共享出来的数据的子组件可以通过static contextType = ContextObject
接收到data
,使用this.context
即可调用data
适用场景
- 很多不同层级的组件需要访问同样的数据,所以如果我们只是想避免层层传递一些属性,那么我们还有更好的选择: 组合组件
Context API 理解与运用
- React.createContext(defaultValue)
创建一个Context对象,defaultValue
是默认参数,在一个组件中可以调用这个对象的Provider
API,并且设置新的参数:
const Context = React.createContext(defaultValue)
function ContextProvider () {
return (
<Context.Provider value = { newValue }>
/* 子组件(这里的组件及其子组件都可以收到这个Context对象发出的newValue) */
<Context.Provider/>
)
}
但是如果没有对应的Context.Provider
相匹配,那么组件树上的所有组件都可以收到这个Context对象发出 的defaultValue
;
同时可以调用Context
的Consumer
API可以用来接受到Context
的值,并且根据这个值渲染组件:
function ContextConsumer () {
return (
<Context.Comsumer>
{value =>
<div>
/* 可以根据value进行渲染 */
</div>
}
</Context.Comsumer>
)
}
-
Context.Provider & Context.Comsumer
-
<MyContext.Provider value={ variableValue }>
可以允许消费组件订阅到variableValue
值的变化,也就是说消费组件可以根据variableValue
值的变化而变化,variableValue
的值我们可以定义一个事件来控制改变;
<MyContext.Consumer> {value => /* 基于 context 值进行渲染*/} </MyContext.Consumer>
利用
Context.Consumer
API 可以让我们即使是在函数式组件也可以订阅到 Context的值;这种方法需要一个函数作为子元素,函数接收当前的context值,并返回一个 React 节点。
传递给函数的
value
值等价于组件树上方离这个 context 最近的 Provider 提供的variableValue
值。如果没有对应的 Provider,value
参数等同于传递给createContext()
的defaultValue
。// Provider 结合 Consumer 使用示例 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // 创建 Context 对象 const MyContext = React.createContext(0) // defaultValue 是数字0 // App组件 渲染 Context 对象 class App extends React.Component { constructor(props){ super(props); this.state = { variableValue : 0 } // 处理 Provider中value变化的函数 this.handleChange = () => { this.setState(state => ({ variableValue: state.variableValue + 1 }) ) } } render(){ return ( // 调用 Context.Provider, 设置可以让Consumer组件监听变化的 value 值 <MyContext.Provider value = {this.state.variableValue}> <Context changeValue = {this.handleChange}/> </MyContext.Provider> ) } } // 消费组件 class Context extends React.Component{ render(){ return ( <MyContext.Consumer> /* 根据Context的value进行渲染 */ {value => <button onClick={this.props.changeValue} > Add MyValue:{value} </button> } </MyContext.Consumer> ) } } ReactDOM.render( <App className = 'app'/> , document.getElementById('root') );
当Provider的
variableValue
值发生变化时,它内部的所有消费组件都会重新渲染。 -
-
Class.contextType
class MyClass extends React.Component { render() { let value = this.context; // this.context 可以访问到 MyClass 的contextType /* 基于 MyContext 组件的值进行渲染 */ } } MyClass.contextType = MyContext; //将MyClass的contextType属性赋值为 Context 对象的值
挂载在 class 上的
contextType
属性会被重赋值为一个由React.createContext()
创建的 Context 对象。此属性能让你使用this.context
来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。注: 从文档的字面意思,
Class.contextType
是类组件特有的API,所以函数式组件只能使用Context.Consumer
来访问Context
对象的值,我们可以来试一下类组件和函数式组件的API:import React from 'react'; import ReactDOM from 'react-dom'; import './index.css'; // 创建 Context 对象 const MyContext = React.createContext(0) // App组件 渲染 Context 对象 class App extends React.Component { constructor(props){ super(props); this.state = { variableValue : 0 } this.handleChange = () => { this.setState(state => ({ variableValue: state.variableValue + 1 }) ) } } render(){ return ( // 调用 Context.Provider, 设置可以让Consumer组件监听变化的 value 值 <MyContext.Provider value = {this.state.variableValue}> <Context_consumer changeValue = {this.handleChange} /> <br/> <Context_contextType changeValue = {this.handleChange} /> <br /> <Func_Consumer changeValue = {this.handleChange} /> <br /> <func_contextType changeValue = {this.handleChange} /> </MyContext.Provider> ) } } // Class & Consumer 消费组件 class Context_consumer extends React.Component{ render(){ return ( <MyContext.Consumer> {value => <button onClick={this.props.changeValue} > Add Class_consumer:{value} </button> } </MyContext.Consumer> ) } } // Class & contextType 的消费组件 class Context_contextType extends React.Component{ render(){ let value = this.context return ( <button onClick={this.props.changeValue} > Add Class_contextType:{value} </button> ) } }; Context_contextType.contextType = MyContext; // 函数组件 & Consumer function Func_Consumer (props) { return ( <MyContext.Consumer> {value => <button onClick={props.changeValue} > Add Func_consumer:{value} </button> } </MyContext.Consumer> ) } // 函数组件 & contextType function func_contextType (props) { let value = this.context return ( <button onClick={props.changeValue} > Add func_contextType:{value} </button> ) } func_contextType.contextType = MyContext; ReactDOM.render( <App className = 'app'/> , document.getElementById('root') );
运行结果:
除了func_contextType组件之外其他组件都可以正常运行 -
Context.displayName
context 对象接受一个名为
displayName
的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。示例,下述组件在 DevTools 中将显示为 MyDisplayName:
const MyContext = React.createContext(/* some value */); MyContext.displayName = 'MyDisplayName'; <MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中 <MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中