Context
Context 设计目的是为了共享那些对于一个组件树而言是“全局”的数据,例如当前认证的用户、主题或首选语言。
它类似vue的状态管理工具vuex,我们所接触的react中数据是通过props 属性自上而下(由父及子)进行传递的,但这种做法对于多层级父子关系的组件传值是极其繁琐的。这里主要讲解的是React 16.3之后的context版本,不在对老版本进行讲解。
context的使用流程:
1、创建context
文件Mycontext
,并引入createContext :
import {createContext} from 'react'
2、在Mycontext文件中
const Mycontext = createContext({
title:'MyContext title'//可以定义初始值
})
3、在需要使用context
的组件Mycomponent
中引入Mycontext,并且声明context,有两种声明方式,然后可以通过this.context
来获取context中的内容
//引入
import Mycontext from './Mycontext'
//声明
export default class Mycomponent extends Component{
//方式一:
static contextType = Mycontext;
...
}
//方式二:
Mycomponent.contextType = MyContext;
4、Proveder和Consumer的使用
context.Provider
的使用:当有多个组件嵌套时👉 父>儿>孙,用context.provider
包裹父组件后,可以将context
上的值传递给所有被包裹的组件树,当使用了context.Provider
后我们在context中所定义的默认值将会失效,就需要在context.Provider
上创建value值来进行数据传递。写法示例:
<Mycontext.Provider value={this.state}>
<h1>parent</h1>
<Child></Child>
</Mycontext.Provider>
context.Consumer
的使用:这里通过context.Consumer
可以跨组件读取某个context数据信息,在被context.Consumer
包裹的元素内也可以再次使用context2.Consumer
进行包裹,可以读取不同的context数据,写法示例:
<Mycontext.Consumer>
{(context) => {
{/*这里context代表了导出的Mycontext文件*/}
return (
<>
<p>title:{context.title}</p>
</>
)
}}
</Mycontext.Consumer>
5、context 对象接受一个名为 displayName
的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。
context.displayName = "Mycontext"
简单实例使用
ReactDevTools 展示的结构信息:
context.js
import {createContext} from 'react'
//引入createContext方法来创建context
const MyContext = createContext({
//定义默认信息
name:'context Name',
title:'MyContext Title'
});
export default MyContext
//导出
context2.js
import {createContext} from 'react'
const Mycontext2 = createContext({
title:'MyContext2 title'
})
Mycontext2.displayName = "context2"
export default Mycontext2
//导出
parent.jsx
import React, { Component } from 'react'
import Child from './child.jsx'
//引入子组件
import Mycontext from './mycontext'
//引入第一个context
export default class parent extends Component {
static contextType = Mycontext;//声明context
constructor(){
super()
//在parent自身所维护的数据
this.state={
name:'parent Name',
title:' parent Title'
}
}
render() {
return (
<div>
{/* 通过Provider来包裹父组件,并将parent的state赋值给value,会屏蔽掉context中的默认数据 */}
<Mycontext.Provider value={this.state}>
<h1>parent</h1>
<Child></Child>
</Mycontext.Provider>
</div>
)
}
}
child.jsx
import React, { Component } from 'react'
import Grandson from './grandson'
//引入孙子组件
export default class child extends Component {
render() {
return (
<div>
<h1>Child</h1>
{/*显示孙子组件*/}
<Grandson></Grandson>
</div>
)
}
}
grandson.jsx
import React, { Component } from 'react'
import Mycontext from './mycontext'
//引入第一个context文件,但是数据已经被parent所代替
import Mycontext2 from './mycontext2'
//引入第二个context文件
export default class grandson extends Component {
// static contextType = Mycontext;
render() {
return (
<div>
<Mycontext.Consumer>
{/*通过Consumer来包裹,下面就可以通过context.的形式拿数据*/}
{context => {
return (
<>
<h3>Grandson</h3>
<p>name:{context.title},title:{context.name}</p>
<Mycontext2.Consumer>
{/*嵌套使用,获取不同的context文件数据*/}
{context2 => {
return (
<>
<p>title:{context2.title}</p>
</>
)
}}
</Mycontext2.Consumer>
</>
)
}}
</Mycontext.Consumer>
</div>
)
}
}
计数组件的实现
我们可以通过一个简单的计数组件来查看在context中对方法的使用,了解一个context使用范式,我们可以在context文件中定义一个公共class来保存公共数据,直接通过provider来注入。
countContext.js
import React,{createContext} from 'react'
const { Provider,Consumer:CountConsumer } = createContext()
//将Provider和Consumer解构出来,并且给Consumer起别名
//定义一个公共类
class CountProvider extends React.Component{
constructor(){
super()
this.state={
count:1//计数器初始值
}
}
increment=()=>{//+1操作
this.setState({
count:this.state.count+1
})
}
decrement=()=>{//-1操作
this.setState({
count:this.state.count-1
})
}
clickHandler=(innerHTML)=>{//按钮点击时触发的事件,根据按钮的内容来判断操作后执行相应增减方法,
if(innerHTML==='+'){
this.increment()
}else{
this.decrement()
}
}
render(){
return(
{/*将解构出的Provider包裹,将value注入*/}
<Provider value={{
count:this.state.count,//暴露计数数字
clickHandler:this.clickHandler//暴露点击事件
}}>
{this.props.children}//将在组件中再次包裹的内容放入
</Provider>
)
}
}
export {
CountProvider,
CountConsumer
}
//暴露被封装的Provieder和起了别名的Consumer
Cart.jsx
import React, { Component } from 'react'
import Count from './count'
import {CountProvider} from './countContext'
//从context里面结构封装好的Provider方法
export default class cart extends Component {
render() {
return (
{/*这里再次通过封装的Provider来包裹一次内容,里面的内容就会在context里面的props.children,包裹后里面就会获得context数据和方法*/}
<CountProvider>
<h1>购物车</h1>
<hr/>
<Count></Count>
</CountProvider>
)
}
}
count.jsx
import React, { Component } from 'react'
import {CountConsumer} from './countContext'
//引入 Consumer,这里只是为了获取到context里面的计数数字
import Button from './Button'
//按钮组件
export default class count extends Component {
constructor(){
super()
}
render() {
return (
<CountConsumer>
{
// 结构出来count
({count})=>{
return(
<>
{/* 两个按钮 */}
<Button>-</Button>
{count}
<Button>+</Button>
</>
)
}
}
</CountConsumer>
)
}
}
我们这里触发事件的方式是
<button onClick={()=>context.someHandler()}>点老子</button>
Button.js
import React from 'react'
import {CountConsumer} from './countContext'
//将Consumer 引入,这里只要时为了获得context事件
const Button = (props)=>{
return(
<CountConsumer>
{
(context)=>{
return(
{/*给按钮添加click事件,将事件源e传入,然后将e.target.innerHTML传入方法的参数*/}
<button onClick={(e)=>context.clickHandler(e.target.innerHTML)}>{props.children}</button>
)
}
}
</CountConsumer>
)
}
export default Button
效果显示:
何时使用Context
Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。如果用组件组合可以解决的问题,就不要使用 Context 。
使用 context 的通用的场景包括管理当前的 locale,theme,或者一些缓存数据。
总结
总结部分摘自:https://blog.csdn.net/Charissa2017/article/details/105746685
- React.createContext(defaultValue) 创建一个 Context 对象。
- Class.contextType,挂载在 class 上的 contextType 属性会被赋值为一个 Context 对象。这能让你使用 this.context 来消费最近的 Context 上的数据。你可以在任何生命周期中访问到它,包括 render 函数中。
- Context.Provider,接收一个 value 属性,传递给消费组件。一个 Provider 可以和多个消费组件有对应关系。多个 Provider 也可以嵌套使用,里层的会覆盖外层的数据。
- Context.Consumer,是函数作为子元素(function as a child)这种做法。这个函数参数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。
- Context.displayName,浏览器中调试用。