一、旧版Context
1、声明Context
上下文:Context,表示做某一些事情的环境
React中的上下文特点:
- 当某个组件创建了上下文后,上下文中的数据,会被所有后代组件共享
- 如果某个组件依赖了上下文,会导致该组件不再纯粹(外部数据仅来源于属性props)
- 一般情况下,用于第三方组件(通用组件)
import React, { Component } from 'react'
import PropTypes from 'prop-types'
export default class OldContext extends Component {
/**
* 约束上下文中数据的类型
*/
static childContextTypes = {
a: PropTypes.number,
b: PropTypes.string.isRequired
}
/**
* 得到上下文中的数据
*/
getChildContext() {
console.log("获取上下文数据");
return {
a: 123,
b: 'abc'
}
}
render() {
return (
<div>
</div>
)
}
}
上面我们创建了一个Context,但并未使用。但实际上在render是Context就已经触发。(同理setState改变状态也会重新触发)
2、使用Context
使用上下文中的数据
要求:如果要使用上下文中的数据,组件必须有一个静态属性 contextTypes,该属性描述了需要获取的上下文中的数据类型
- 可以在组件的构造函数中,通过第二个参数,获取上下文数据(但是构造函数只会执行一次,不能实时获取最新状态)
//类子组件B
class ChildB extends React.Component {
/**
* 声明需要使用哪些上下文中的数据
*/
static contextTypes = types
constructor(props, context){
supper(props);
console.log(context);
}
render() {
return <p>
ChildB,来自于上下文的数据:a: {this.context.a}, b:{this.context.b}
</p>
}
}
- 从组件的context属性中获取
- 在函数组件中,通过第二个参数,获取上下文数据
import React, { Component } from 'react'
import PropTypes from "prop-types";
const types = {
a: PropTypes.number,
b: PropTypes.string.isRequired,
onChangeA: PropTypes.func
}
function ChildA(props, context) {//函数组件第二个参数使用
return <div>
<h1>ChildA</h1>
<h2>a:{context.a},b:{context.b}</h2>
<ChildB />
</div>
}
ChildA.contextTypes = types;//函数组件A描述自己需要的上下文中的数据
class ChildB extends React.Component {
/**
* 声明需要使用哪些上下文中的数据
*/
static contextTypes = types
render() {
return <p>
ChildB,来自于上下文的数据:a: {this.context.a}, b:{this.context.b}
</p>
}
}
export default class OldContext extends Component {
/**
* 约束上下文中数据的类型
*/
static childContextTypes = types
state = {
a: 123,
b: "abc"
}
/**
* 得到上下文中的数据
*/
getChildContext() {
console.log("获取新的上下文");
return {// 这里通过state为上下文,方便后面改变状态。如果不要后续改变状态,也可以使用静态的值,而不用固定使用state
a: this.state.a,
b: this.state.b
}
}
render() {
return (
<div>
<ChildA />
<button onClick={() => {
this.setState({
a: this.state.a + 1
})
}
}>a加1</button>
</div>
)
}
}
在父组件中改变状态,getChildContext会重新触发,而子组件也会获取新的上下文。
上下文的数据变化
上下文中的数据不可以直接变化,最终都是通过状态改变
在上下文中加入一个处理函数,可以用于后代组件更改上下文的数据
class ChildB extends React.Component {
/**
* 声明需要使用哪些上下文中的数据
*/
static contextTypes = types
render() {
return <p>
ChildB,来自于上下文的数据:a: {this.context.a}, b:{this.context.b}
<button onClick={() => {
this.context.onChangeA(this.context.a + 2);
}}>子组件的按钮,a+2</button>
</p>
}
}
export default class OldContext extends Component {
/**
* 约束上下文中数据的类型
*/
static childContextTypes = types
state = {
a: 123,
b: "abc"
}
/**
* 得到上下文中的数据
*/
getChildContext() {
console.log("获取新的上下文");
return {
a: this.state.a,
b: this.state.b,
onChangeA: (newA) => {
this.setState({
a: newA
})
}
}
}
render() {
return (
<div>
<ChildA />
<button onClick={() => {
this.setState({
a: this.state.a + 1
})
}
}>a加1</button>
</div>
)
}
}
通过上下文给予的改变上下文中状态的方法去改变上下文中的状态值。
3、多层上下文问题
import React, { Component } from 'react'
import PropTypes from "prop-types";
const types = {
a: PropTypes.number,
b: PropTypes.string.isRequired,
onChangeA: PropTypes.func
}
class ChildA extends Component {
static contextTypes = types;
static childContextTypes = {
a: PropTypes.number,
c: PropTypes.string
}
getChildContext() {
return {
a: 789,
c: "hello"
}
}
render() {
return <div>
<h1>ChildA</h1>
<h2>a:{this.context.a},b:{this.context.b}</h2>
<ChildB />
</div>
}
}
class ChildB extends React.Component {
/**
* 声明需要使用哪些上下文中的数据
*/
static contextTypes = {
...types,
c: PropTypes.string
}
render() {
return <p>
ChildB,来自于上下文的数据:a: {this.context.a}, b:{this.context.b}
,c: {this.context.c}
<button onClick={() => {
this.context.onChangeA(this.context.a + 2);
}}>子组件的按钮,a+2</button>
</p>
}
}
export default class OldContext extends Component {
/**
* 约束上下文中数据的类型
*/
static childContextTypes = types
state = {
a: 123,
b: "abc"
}
/**
* 得到上下文中的数据
*/
getChildContext() {
console.log("获取新的上下文");
return {
a: this.state.a,
b: this.state.b,
onChangeA: (newA) => {
this.setState({
a: newA
})
}
}
}
render() {
return (
<div>
<ChildA />
<button onClick={() => {
this.setState({
a: this.state.a + 1
})
}
}>a加1</button>
</div>
)
}
}
从代码中可以看到,我们在OldContext
和ChildA
组件中创建了上下文,此时如果我们在ChildB
使用OldContext
上下文给予的改变状态的方法,会怎么样呢?
我们发现childA
虽然创建了上下文,但是自己并不会使用(上下文是给子组件使用的)。而ChildB
就近原则使用的是ChildA
的上下文;此外,由于OldContext
上下文给予的方法是改变当前的上下文的a
的值,此时childB
使用的是childA
的上下文a
,所以此时改变后的OldContext
上下文中的a
为791.
博主开始运营自己的公众号啦,感兴趣的可以关注“飞羽逐星”微信公众号哦,拿起手机就能阅读感兴趣的博客啦!