前言
为了让组件之间数据传递更加的方便与快捷,React提供了React Context技术。该技术能够简洁的在组件之间传递数据,不仅能完成父子组件之间数据的传递,也能够在多层次组件之间传递数据。本篇文章将详细地讲解React Context技术并展示该技术的功能案例。
在开始正式讲解之前, 再次强调React Context的功能:实现组件之间的数据传递。
一、React Context的基本用法
要想使用React Context技术,首先要使用React.createContext()方法创建React Context对象,该对象包括两个组件:Provider和Consumer。这两个组件可以使用解构的方式定义出来。代码如下所示。
const {Provider,Consumer} = React.createContext();
1、Provider组件
Provider组件用来提供传递数据的来源,被称为“生产组件”。该组件具备一个value属性,该属性的取值就是要传递的数据,使用格式如下所示。
<Provider value={}></Provider>
当Provider组件的value属性绑定的变量发生变化时,所有的消费组件都会被重新渲染。
2、Consumer组件
Consumer组件用来接收传递过来的数据,被称为“消费组件”。该组件内部是一个箭头函数,函数的参数用来接收生产组件传递过来的数据,使用格式如下所示。
<Consumer>
{
para=>{
return ();
}
}
</Consumer>
3、基本使用案例
例1:父组件将其state区中的data数据跨越中间件传递给子组件。
const {Fragment,Component}=React;
const {Provider,Consumer}=React.createContext();
//父组件App
class App extends Component{
constructor(props){
super(props);
this.state={
data:'小海前端'
}
}
btnClick(){
this.setState({
data:'React框架'
})
}
render(){
return(
<Fragment>
<Provider value={this.state.data}>
<button onClick={()=>this.btnClick()}>更改数据</button>
<p>父组件的数据:{this.state.data}</p>
<Middle></Middle>
</Provider>
</Fragment>
)
}
}
//中间件
function Middle(props){
return (
<Child></Child>
)
}
//子组件Child
class Child extends Component{
render(){
return(
<Consumer>
{
para=>{
return (
<Fragment>
<p>子组件的数据:{para}</p>
</Fragment>
)
}
}
</Consumer>
)
}
}
ReactDOM.render(
<App></App>,
document.querySelector('#app')
);
在这个例子中,数据的传递顺序是:父组件App -> 中间件Middle -> 子组件Child。在此中间件也可以不设置,本例中只是为了说明,通过React Context可以跨组件进行数据传递。
二、滑动门案例
例2:制作一个如下图所示的滑动门效果。该案例将分为两个组件,父组件App用来实现整个案例的布局和左侧数据的遍历。子组件Detail用来实现右侧具体商品信息的展示。
首先,想通过数据模拟四款电子商品的品牌、大小、颜色、单价等数据。
const phone=[
{
brand:'iPhone 12',
size:256,
color:'深空灰',
price:5800
},
{
brand:'iPad Air 4',
size:512,
color:'时尚绿',
price:4499
},
{
brand:'iPad Pro',
size:512,
color:'土豪金',
price:7899
},
{
brand:'iPad Mini',
size:64,
color:'黑色',
price:2799
}
];
1、父组件App的实现
在父组件的state区设置一个名为pro的数据,该数据存储以下两个信息。
- isOk:表示用户是否鼠标经过了某个左侧商品项。默认情况下没有经过,所以初值为false。
- detail:表示用户鼠标经过左侧商品时,该商品的详情信息。默认情况下是空对象。
同时这个pro数据也是父组件App要向子组件Detail传递的数据,也就是生产组件中value属性要绑定的变量。当鼠标经过任意商品项时,改变pro的值,这样接收生产组件的消费组件也会重新渲染。
class App extends Component {
constructor(props){
super(props);
this.state={
pro:{
isOk:false,
detail:{
brand:'',
size:'',
color:'',
price:''
}
}
}
}
getInfo(index){
this.setState({
pro:{
isOk:true,
detail:phone[index]
}
})
}
render(){
return (
<Fragment>
<Provider value={this.state.pro}>
<div className="phone">
<div className="left">
<ul>
{
phone.map((item,index)=>{
return (
<li onMouseOver={()=>this.getInfo(index)} key={index}>{item.brand}</li>
)
})
}
</ul>
</div>
<div className="right">
<Detail></Detail>
</div>
</div>
</Provider>
</Fragment>
)
}
}
2、子组件Detail的实现
子组件中设置一个消费组件,其中箭头函数的参数product就是接收生产组件传递过来的数据。通过该数据中的isOk来判断鼠标是否经过了某个商品项。如果isOk取值为true,则将最终的商品细节显示在页面中,否则显示空字符串。这样可以保证项目一运行,右侧的商品细节中不会显示任意一款商品的信息。
class Detail extends Component {
render(){
return (
<Consumer>
{
product=>{
return (
<Fragment>
<h4>请从左侧选择一款商品:</h4>
{
product.isOk
?
<div>
<p>品牌:{product.detail.brand}</p>
<p>大小:{product.detail.size}</p>
<p>颜色:{product.detail.color}</p>
<p>单价:{product.detail.price}</p>
</div>
:
''
}
</Fragment>
)
}
}
</Consumer>
)
}
}
三、多Context数据的使用
若需要使用多个Context数据,则必须为React.createContext()方法生成的变量起一个名字,而不能直接对其进行解构的。 例如要设置两个不同的Context数据,代码如下所示。
const schoolContext = React.createContext();
const studentContext = React.createContext();
这时,生产组件Provider的使用方法如下所示。
<schoolContext.Provider value={}></schoolContext.Provider>
<studentContext.Provider value={}></studentContext.Provider>
消费组件Consumer的使用方法如下所示。
<schoolContext.Consumer></schoolContext.Consumer>
<studentContext.Consumer></stucentContext.Consumer>
例3:用多Context的方法改造例2中的滑动门效果。
在例2中,我们为了传递鼠标是否经过某商品项和鼠标经过商品项是的商品细节,将这两部分信息都合成在了一个名为pro的state数据中。有了多Context技术,我们就可以将这两部分的信息分成两个不同的Context数据来进行传递。
const {Fragment,Component}=React;
//定义两个Context数据
const isOkContext=React.createContext();
const productContext=React.createContext();
//父组件App
class App extends Component {
constructor(props){
super(props);
this.state={
isOk:false,
pro:{
brand:'',
size:'',
color:'',
price:''
}
}
}
getInfo(index){
this.setState({
isOk:true,
pro:phone[index]
})
}
render(){
return (
<Fragment>
<isOkContext.Provider value={this.state.isOk}>
<productContext.Provider value={this.state.pro}>
<div className="phone">
<div className="left">
<ul>
{
phone.map((item,index)=>{
return (
<li onMouseOver={()=>this.getInfo(index)} key={index}>{item.brand}</li>
)
})
}
</ul>
</div>
<div className="right">
<Detail></Detail>
</div>
</div>
</productContext.Provider>
</isOkContext.Provider>
</Fragment>
)
}
}
//子组件:包括右侧具体一组数据的显示
class Detail extends Component {
render(){
return (
<isOkContext.Consumer>
{
isOk=>(
<productContext.Consumer>
{
product=>{
return (
<Fragment>
<h4>请从左侧选择一款商品:</h4>
{
isOk
?
<div>
<p>品牌:{product.brand}</p>
<p>大小:{product.size}</p>
<p>颜色:{product.color}</p>
<p>单价:{product.price}</p>
</div>
:
''
}
</Fragment>
)
}
}
</productContext.Consumer>
)
}
</isOkContext.Consumer>
)
}
}
ReactDOM.render(
<App></App>,
document.querySelector('#app')
);
总结
本文是React系列教程的第九篇文章,主要为大家讲解了React Context的基础用法。该技术提供了生产组件<Provider>和消费组件<Consumer>的概念,实现了更加方便的在组件之间传递数据的方式。本次讲解的是React Context技术的基本用法,下一篇文章会讲解React Context在修改页面主题功能上的应用,以及ContextType技术的简化写法,并为大家演示带有默认值的Context是如何使用的。
关于作者
小海前端,具有18年Web项目开发和前后台培训经验,在前端领域著有较为系统的培训教材,对Vue.js、微信小程序开发、uniApp、React等全栈开发领域都有较为深的造诣。入住今日头条,希望能够更多地结识Web开发领域的同仁,将Web开发大力地进行普及。同时也愿意与大家进行深入的技术研讨和商业合作。