父子组件通信
顾名思义就是父组件和子组件之间进行通信交流。下面先看样例代码:
// 父子组件通信
import React from "react";
// 下面为父子组件通信实例代码
// 父组件
class ParentSon extends React.Component {
constructor(props) {
super(props);
this.state = {
msg: "father",
name: "partent",
age: 60
};
}
callback = (msg, name, age) => {
this.setState({ msg });
this.setState({ name });
this.setState({ age });
};
render() {
return (
<div style={{padding: 30+'px'}}>
<h1>{this.state.msg}</h1>
<Child
callback={this.callback}
age={this.state.age}
name={this.state.name}
></Child>
</div>
);
}
}
// 子组件
class Child extends React.Component{
constructor(props){
super(props);
this.state={
msg: 'I am son',
name: 'son',
age: 8
};
// 下面一行代码是为了解决非箭头函数使用时this的指向问题不能指向当前子组件自身
// this.change=this.change.bind(this);
}
// change(){
// // console.log(this);
// this.props.callback(this.state.msg,this.state.name,this.state.age);
// }
change=()=>{
this.props.callback(this.state.msg,this.state.name,this.state.age);
}
render(){
return (
<div>
<div>{this.props.name}</div>
<div>{this.props.age}</div>
<button onClick={this.change}>点击</button>
</div>
)
}
}
export default ParentSon;
如上面代码所示,分别创建了父组件 ParentSon 和子组件 Child ,父组件里面定义了自己的state内容,并定义了一个 callback 方法,此方法用来改变父组件自身的state内容值的,然后render函数里面除了渲染自身state里的msg值之外,将自身的callback方法以及自身state里的name,age分别作为参数传给了子组件 Child。
下面再继续看子组件内部,除了有自己的state之外,还有一个change方法,然后render函数里渲染了来自props里的值以及一个点击事件按钮,而这个按钮点击事件就会触发调用自身那个change事件,在进入change方法内部会看到,实际上,执行的就是props接收的callback方法,也就是从父组件传过来的那个callback,而这里的参数又是子组件自身state的内容值,到此,我们可以顺一下整个思路了。
思路: 一开始渲染出的全是父组件的值(因为子组件显示的就是来自父组件的值)-----> 点击按钮 -----> 触发子组件change事件(实际接收的父组件callback方法)-----> 父组件callback被调用,父组件的state里的值都发生改变,此时已变成和子组件state里一样的值了 -----> state改变后的值,又通过props传给了子组件Child -----> 再一次渲染出来传过来的值,即和子组件一模一样的值,此时整个过程全部结束。
跨级组件通信
顾名思义就是互不相干且之间隔了好几级的一种通信交流方式。下面先看样例代码:
// 跨级组件通信
import React from "react";
const ThemeContext = React.createContext("light");
class ContextMess extends React.Component {
static contextType = ThemeContext;
render() {
return (
<ThemeContext.Provider value="dark">
<div style={{padding: 30+'px'}}>
<h3>{this.context}</h3>
<Toolbar></Toolbar>
</div>
</ThemeContext.Provider>
);
}
}
class Toolbar extends React.Component {
render() {
return (
<div>
<ThemedButton></ThemedButton>
</div>
);
}
}
class ThemedButton extends React.Component {
static contextType = ThemeContext;
render() {
return (
<div>
<h1>{this.context}</h1>
<ThemeContext.Consumer>
{value => (<h6>{value}</h6>)}
</ThemeContext.Consumer>
</div>
);
}
}
export default ContextMess;
从上面代码可以看出,有三个组件,ContextMess ,Toolbar ,ThemedButton ,ContextMess 为最外层组件,依次是Toolbar 组件,再是ThemedButton 组件,逐层嵌套。
代码中主要使用了react中的Context技术点,使用了Context中的以下几个API:React.createContext,Context.Provider,Class.contextType,Context.Consumer。
React.createContext: 创建一个 Context 对象。当 React 渲染一个订阅了这个 Context 对象的组件,这个组件会从组件树中离自身最近的那个匹配的 Provider 中读取到当前的 context 值。
Context.Provider: 每个 Context 对象都会返回一个 Provider React 组件,它允许消费组件订阅 context 的变化;Provider 接收一个 value 属性,传递给消费组件;当 Provider 的 value 值发生变化时,它内部的所有消费组件都会重新渲染。
Class.contextType: (代码中通过 static 来定义静态属性contextType,即组件(class)自己的属性,等同于class.contextType)挂载在 class 上的 contextType 属性会被重赋值为一个由 React.createContext() 创建的 Context 对象。这能让你使用 this.context 来消费最近 Context 上的那个值。你可以在任何生命周期中访问到它,包括 render 函数中。
Context.Consumer: 这里,React 组件也可以订阅到 context 变更。这能让你在函数式组件中完成订阅 context;这需要函数作为子元素(function as a child)这种做法。这个函数接收当前的 context 值,返回一个 React 节点。传递给函数的 value 值等同于往上组件树离这个 context 最近的 Provider 提供的 value 值。如果没有对应的 Provider,value 参数等同于传递给 createContext() 的 defaultValue。
思路: 简单来说,那个‘light’一开始创建的就相当于要在其他组件使用的数据,在 ContextMess 组件中通过使用 contextType 获取 createContext 创建的对象,然后通过this.context来获取值,即“light”;除此之外,使用 Context.Provider API 通过value参数将当前值改为“dark”,然后不使用 props 传值方式,在二级组件中可以看到,并没有使用props接收值,最后在下一级组件通过 Class.contextType 和 this.context 方式读取值,除此之外还使用 Context.Consumer API 来获取最近的那个匹配的context值,即改变后的值“dark”。