在使用react过程中,不可避免的需要组件间的数据通信,数据通信一般情况有一下几种情况:
下面将依次来说一下这几种组件间通信的解决办法。
这种通信方式是最常见的一种,解决方法就是通过props来进行通信,子组件接收到props后再进行相应的处理。
import React,{Component} from 'react';
import Header from './header'
import './father.less';
class Father extends Component{
constructor(){
super();
this.state={
myName:"张三"
}
}
render(){
return (
<div className="container">
<Header title={this.state.myName}/>
</div>
)
}
}
export default Father
上面代码为father组件,在其内部引入了header子组件,并将自己的state中的myName传递给header组件,定义名称为title,在子组件中可以通过this.props.title来获取到值。
import React,{Component} from 'react';
import './header.less';
class Header extends Component{
render(){
return (
<div className="components-header row">
{this.props.myName}
</div>
)
}
}
export default Header
上述代码就是一个简单的父组件向子组件来传递数据。当然,为了保证程序的严谨性,在子组件中我们可以对传递过来的props进行类型校验,如果类型校验没有通过,则会抛出一个错误,已提醒调用组件者。
import React,{Component} from 'react';
import './header.less';
class Header extends Component{
propsType:{
title:React.propsTypes.String
}
render(){
return (
<div className="components-header row">
{this.props.myName}
</div>
)
}
}
export default Header
如此即完成了对于传递过来的参数校验。
子组件向父组件通信可以通过回调函数的方式来进行,我们还是将上面的代码来修改一下。
先看father组件
import React,{Component} from 'react';
import Header from './header'
class Father extends Component{
constructor(){
super();
this.state={
myName:"张三"
}
}
showChildName(name){
console.log(name);
}
render(){
return (
<div className="container">
<Header showName={this.showChildName.bind(this)}/>
</div>
)
}
}
export default Father
其次是子组件
import React,{Component} from 'react';
import './header.less';
class Header extends Component{
propsType:{
showName:React.propsTypes.Func
}
constructor(){
super();
this.state={
myName:"header"
}
}
showName(){
let myName = this.state.myName;
this.props.showName(myName)
}
render(){
return (
<div className="components-header row">
<button onClick={this.showName.bind(this)}>按钮</button>
</div>
)
}
}
export default Header
所谓跨级组件通信,就是父组件向子组件的子组件通信,向更深层的子组件通信。跨级组件通信可以采用下面两种方式:中间组件层层传递props;使用context对象
对于第一种方式,如果父组件结构较深,那么中间的每一层组件都要去传递 props,增加了复杂度,并且这些 props 并不是这些中间组件自己所需要的。不过这种方式也是可行的,当组件层次在三层以内可以采用这种方式,当组件嵌套过深时,采用这种方式就需要斟酌了。 使用 context 是另一种可行的方式,context 相当于一个全局变量,是一个大容器,我们可以把要通信的内容放在这个容器中,这样一来,不管嵌套有多深,都可以随意取用。 使用 context 也很简单,需要满足两个条件:
下面请看一个例子
import React, { Component } from 'react';
import Sub from "./Sub";
import "./App.css";
export default class App extends Component{
// 父组件声明自己支持 context
static childContextTypes = {
color:React.propTypes.string,
callback:React.propTypes.func,
}
// 父组件提供一个函数,用来返回相应的 context 对象
getChildContext(){
return{
color:"red",
callback:this.callback.bind(this)
}
}
callback(msg){
console.log(msg)
}
render(){
return(
<div>
<Sub></Sub>
</div>
);
}
}
sub组件
import React from "react";
import SubSub from "./SubSub";
const Sub = (props) =>{
return(
<div>
<SubSub />
</div>
);
}
export default Sub;
subsub组件(孙子组件)
import React,{ Component } from "react";
import PropTypes from "prop-types";
export default class SubSub extends Component{
// 子组件声明自己需要使用 context
static contextTypes = {
color:PropTypes.string,
callback:PropTypes.func,
}
render(){
const style = { color:this.context.color }
const cb = (msg) => {
return () => {
this.context.callback(msg);
}
}
return(
<div style = { style }>
SUBSUB
<button onClick = { cb("孙子组件信息") }>按钮</button>
</div>
);
}
}
跨组件通信的方式适用于以上所有的通信方式,这种方式是通过发布/订阅者模式来实现,需要安装PubSub
首先是通过npm来安装pubsub
npm install pubsub-js --save
页面中引入
import PubSub from 'pubsub-js'
pubsub有三中操作,分别是发布消息,订阅消息,取消订阅
发送消息:PubSub.publish(名称,参数)
订阅消息:PubSub.subscrib(名称,函数)
取消订阅:PubSub.unsubscrib(名称)
首先发送消息需要顶一个名称,以供给订阅消息的名称来确定订阅哪个消息,基本上是一个键值对的形式,参数是该键的值,当在组件中注册了订阅消息以后,相当与注册了一个监听事件,当有发布消息发出,订阅消息就会接收到,并在订阅消息的函数中进行自定义处理。取消订阅相当于是取消该监听事件。
home组件
import React, { Component } from 'react';
import PubSub from 'pubsub-js';
class Home extends Component {
constructor(props){
super(props);
this.state={
increase:'increase',
decrease:'decrease'
}
}
buttonIncrease(){
PubSub.publish('PubSubmessag',this.state.increase);
}
buttonDecrease(){
PubSub.publish('PubSubmessage', this.state.decrease);
}
render() {
return (
<div>
Some state changes:
<button onClick={this.buttonIncrease.bind(this)}>Increase</button>
<button onClick={this.buttonDecrease.bind(this)}>Decrease</button>
</div>
)
}
}
export default Home;
子组件
import React, { Component } from 'react';
import { Link} from 'react-router-dom';
import PubSub from 'pubsub-js';
export default class App extends Component{
constructor(props){
super(props);
this.state={
increase:'none',
}
}
componentDidMount(){
this.pubsub_token = PubSub.subscribe('PubSubmessage', function (topic,message) {
this.setState({
increase: message
});
}.bind(this));
}
componentWillUnmount(){
PubSub.unsubscribe(this.pubsub_token);
}
render() {
return (
<div>
<header>
Links:
<Link to="/App/home">Home</Link>
</header>
<div style={{ marginTop: '1.5em' }}>{ this.props.children}</div>
<div style={{ marginTop: '1.5em' }}>{ this.state.increase}</div>
</div>
)
}
}