react组件间的通信
在使用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 也很简单,需要满足两个条件:
- 上级组件要声明自己支持 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> ) } }