上节课我们介绍了Fragment
这个东西可以代替我们的div
标签,但是在渲染时会把标签丢弃掉,只保留标签体内容,这节课我们来介绍一下关于context
的相关内容。
回顾
在这节课内容开始之前,我们对上节课内容做一个简单的回顾
Fragment
可以使得渲染时不产生不必要的div
,空标签也可以达到相同效果Fragment
可以且仅可以接收key
值,但是空标签什么都不能接
以上便是上节课的内容,接下来我们来介绍一下Context
什么是context
我们还记不记得我们在看类组件的时候看见组件实例对象上除了三大属性之外,还有一个context
属性?其实这个是组件间的一种通信方式,通常用于祖组件
和后代组件
之间
什么意思呢?比如说这样:A
组件有一个子组件B
,而B
组件还有一个子组件C
,我们知道A
组件要想给B
组件传递数据可以通过props
,但是如果A
想给C
传递数据,是不是要先通过props
传给B
,然后B
再通过props
传给C
?这样做的话也太麻烦了。而context
就可以使得A
直接把数据传给C
。
Context
那么我们知道context
的作用了,那么我们应该这么去使用呢?我们来看一下:
export default class A extends Component { state = {name:"jingxun"} render() { return ( <div> <h3>Component A</h3> <h4>name: {this.state.name}</h4> <B /> </div> ); } } class B extends Component { render() { return ( <div> <h3>Component B</h3> <h4>Recive name from A: ?????</h4> <C /> </div> ); } } class C extends Component { render() { return ( <div> <h3>Component C</h3> <h4>Recive name from A: ?????</h4> </div> ); } }
首先我们定义了三个组件,但是我们只渲染A
组件,A
组件中渲染B
组件,B
组件中渲染C
组件,我们在三个组件中都想展示A
的state
中的name
,但是B
和C
组件中我们先用问号代替,因为我们还没有传任何东西给这两个组件。那么我们来看一下效果:
我们看到页面上成功展示了我们想要的信息,而且我们加了样式,三个组件的父子关系都能很清晰的展示出来,那么,我们怎么给B
组件还有C
组件传递数据呢?首先给B
组件传数据是不是非常简单啊?直接一个props
进去就行了?问题是不是在C
组件?给C
组件传递数据的话我们目前来说,第一是不是也可以让B
组件给C
传一个props
?另外PubSub
包括redux
是不是都可以啊?但是我们来看现在我们用context
怎么传:
const NameContext = React.createContext(); export default class A extends Component { state = { name: "jingxun" } render() { return ( <div className="parent"> <h3>Component A</h3> <h4>name: {this.state.name}</h4> <NameContext.Provider value={this.state.name}> <B /> </NameContext.Provider> </div> ); } } class B extends Component {...} class C extends Component { render() { console.log(this); return (...); } }
我们来看一下上面这段代码,我们先把不需要关注的代码隐藏掉,我们做了什么?是不是定义了一个NameContext
?可能大家有人已经注意到了,我们这里首字母大写了。那么是什么意思呢?这就说明这是一个组件,通React.createContext
方法创建出来的一个组件,这个组件中有一个Provider
,我们利用NameContext.Provider
标签来为该标签内包裹的所有层级的组件都提供一个value
,大家再学redux
的时候我们是不是就已经说过这个东西了?用Provider
来提供store
,这里也是一样,我们用Provider
来提供context
,那么我们现在来看看C
组件中能不能接收到吧。打印一下C
组件的this
:
我们来看一下,诶好像没有啊,这是怎么回事呢?因为我们并不是说用Provider
一包就一劳永逸了,我得在被包裹的组件以及子组件中声明,只有声明了才能使用,否则的话context
里面是接不到东西的。那么我们在C
组件中声明一下试试看:
class C extends Component { static contextType = NameContext; render() { console.log(this); return ( <div className="grand"> <h3>Component C</h3> <h4>Recive name from A: ?????</h4> </div> ); } }
我们来看这段代码,我们之前说声明是不是不知道怎么声明?那么我们来看,我们在C
组件中只要加一行代码就是我们声明一个静态属性叫contextType
,这个属性就是我们之前定义的Context
组件,这个和PubSub
有些类似,我们有消息发布者,是不是要有订阅者啊?所以说这一步操作就相当于是订阅,那么我们这次再来看一下:
这次我们成功拿到了context
的数据,但是这是个字符串,我们组件的context
属性是不是默认是个对象类型啊?所以说我们Provider
标签在提供value
的时候直接传一个对象就符合规范了。
const NameContext = React.createContext(); export default class A extends Component { state = { name: "jingxun" } render() { const { name } = this.state; return ( <div className="parent"> <h3>Component A</h3> <h4>name: {name}</h4> <NameContext.Provider value={{name}}> <B name={name}/> </NameContext.Provider> </div> ); } } class B extends Component { render() { return ( <div className="child"> <h3>Component B</h3> <h4>Recive name from A: {this.props.name}</h4> <C /> </div> ); } } class C extends Component { static contextType = NameContext; render() { return ( <div className="grand"> <h3>Component C</h3> <h4>Recive name from A: {this.context.name}</h4> </div> ); } }
我们来看这段完整代码,A
通过props
传数据给B
,但是Provider
给B
以及B
的后代组件都提供了context
,但是B
没有用,B
的子组件C
在组件中声明使用了context
。那么我们来看一下效果:
这样我们便实现了我们之前所定的需求,但是还有一个问题,如果我们的C
组件的是一个函数式组件怎么办?函数式组件有this
吗?函数式组件有静态属性吗?这俩东西函数式组件是不是都没有啊?那么函数式组件是不是就不能用context
了呢?其实并不是,我们来看:
function C(){ return ( <div className="grand"> <h3>Component C</h3> <h4>Recive name from A: <NameContext.Consumer> {value => value.name} </NameContext.Consumer> </h4> </div> ); }
我们来看一下,我们是不是把C
组件改成了一个函数式组件?但是我们的函数式组件并没有静态属性之类的东西,没有关系,Context
组件中还有另一个组件标签,就是Consumer
,这是什么呢?我们说了Provider
是提供者,那么有提供者自然就要有使用者嘛,我们在这个标签中直接写一个函数,这个函数接收一个参数value
,这个value
就是我们在Provider
标签中传入的value
属性,然后这个函数返回我们想要展示的值就可以了,我们来看一下效果:
C
这次也依然能成功展示A
传过来的值。而且这个方法在类组件中也能用。
总结
context
可以让祖组件
和后代组件
之间通信- 通过
React.createContext
方法创建Context
组件 - 通过
<Xxxxx.Provider value={{key:value}}>
包裹组件标签来给组件以及其子组件传递context
- 通过在组件中声明
static contextType
来接收context
,但是只适用于类组件 - 通过
<Xxxxx.Consumer>
标签包裹函数来使用context
的数据,还方法使用与所有组件
Copyright statement:The articles of this site are all original if there is no special explanation, indicate the source please when you reprint.
Link of this article:https://work.lynchow.com/article/extend_context/