React 组件间的通信示例


Posted in Javascript onJune 14, 2018

前言

从官网上也有介绍组件间如何通信,但不够详细,这里做个小结,方便对比和回顾

本文内容

处理组件之间的通信, 主要取决于组件之间的关系,因此我们划分为以下三种:

  1. 【父组件】向【子组件】传值;
  2. 【子组件】向【父组件】传值;
  3. 【组件A】向无关系【组件B】传值,一般为兄弟组件;

一、「父组件」向「子组件」传值

这是最普遍的用法,实现上也非常简单,主要是利用props来实现

// 父组件
import React from 'react';
import Son from './components/son';
class Father extends React.Component {
  constructor(props) {
    // 这里要加super,否则会报错
    super(props);
    this.state = {
      checked: true
    }
  }

  render() {
    return (
      <Son text="Toggle me" checked={this.state.checked} />
    )
  }
}
// 子组件
class Son extends React.Component {
  render() {
    // 接收来自父组件的参数
    let checked = this.props.checked,
      text = this.props.text;
    return (
      <label>{text}: <input type="checkbox" checked={checked} /></label>
    )
  }
}

多想一点:

如果组件的嵌套层次太多,那么从外到内的交流成本就会加深,通过 props 传值的优势就不明显,因此,我们还是要尽可能的编写结构清晰简单的组件关系, 既也要遵循组件独立原则,又要适当控制页面,不可能或极少可能会被单用的代码片,可不编写成一个子组件

二、「子组件」向「父组件」传值

我们知道,react的数据控制分为两种,为 props 和 state;其中,props 如上刚介绍过,它是父组件向子组件传值时作为保存参数的数据对象;而 state 是组件存放自身数据的数据对象。这两者最主要的区别就是,props属于父组件传给子组件的只读数据,在子组件中不能被修改,而state在自身组件中使用时,可以通过setState来修改更新。

子组件向父组件传值,需要控制自己的state,并发起父组件的事件回调来通知父组件

// 父组件
import Son from './components/son';
class Father extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      checked: false
    }
  }
  onChildChanged() {
    this.setState({
      checked: newState
    })
  }

  render() {
    let isChecked = this.state.checked ? 'yes' : 'no';
    return (
      <div>
        <span>Are you checked: {isChecked }</span>
        <Son text="Toggle me" 
           initialChecked={this.state.checked}
           callbackParent={this.onChildChanged.bind(this)}
         ></Son>
      </div>
    )
  }
}
// 子组件
class Son extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      checked: this.props.initialChecked
    }
  }
  onTextChange() {
    let newState = !this.state.check.checked;
    this.setState({
      checked: newState
    });
    // 注意,setState 是一个异步方法,state值不会立即改变,回调时要传缓存的当前值,   
    // 也可以利用传递一个函数(以上传的是对象),并传递prevState参数来实现数据的同步更新
    this.props.callbackParent(newState);
  }
  render() {
    let text= this.props.text;
    let checked = this.state.checked;
    return (
      <label>{text}: <input type="checkbox" checked={checked} onChange={this.onTextChange.bind(this)}></label>
    )
  }
}

多想一点:

  1. 同样应当避免深层次的组件嵌套
  2. 这里其实是依赖了props来传递事件的引用,并通过回调的方式来实现,在没有使用工具情况下,可以使用该办法

拓展一点:

在onChange 事件或者其他React事件中,你能获取以下信息:

  1. 「this」 指向你的组件
  2. 「一个参数」 一个react合成事件, SyntheticEvent

我们知道,React对所有事件的管理都是自己封装实现的,html中的 onclick 被封装成了 onClick, onchange 被封装成了 onChange。从根本上来说,他们都是被绑定在body上的。

多个子组件回调同一个回调函数情况

父组件中大概率包含多个子组件,为节省和简洁代码,遵循 don't repeat yourself 原则,我们会让一个回调函数实现多个子组件的功能,或多个组件协作完成指定功能

import React from 'react';
import Son from './components/son';
class Father extends React.Componnet {
  constructor(props) {
    super(props);
    this.state = {
      totalChecked: 0
    }
  }
  onChildChangeed() {
    let newTotal = this.state.totalChecked + (new State ? 1 : -1 );
    this.setState({
       totalChecked = this.state.totalChecked;
    });
  }
  render() {
    return (
      <div>
        <div>Checked numbers: {this.state.totalChecked}</div>
        <Son text="Toggle me" initialChecked={this.state.checked} callbackParent={this.onChildChanged} />
        <Son text="Toggle me too" initialChecked={this.state.checked} callbackParent={this.onChildChanged} />
         <Son text="Add me" initialChecked={this.state.checked} callbackParent={this.onChildChanged} />
      </div>
    )
  }
}
// 子组件
class Son extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      checked: this.props.initialChecked
    }
  } 

  onTextChange() {
    let newState = !this.state.checked;
    this.setState({
      checked: newState
    })
    // setState异步方法问题,注意传值
    this.props.callbackParent(newState);
  }

  render() {
    let text = this.props.checked;
    let checked = this.state.checked;
    return {
      <label>{text}: <input type="checkbox" checked={checked} onChange={this.onTextChange.bind(this)} /></label>
    }
  }
}

多想一点:

在本案例中,我们引用了三个 Son 子组件, 每个 Son 组件都独立工作互不干扰,该例中,增加了一个 totalChecked 来替代之前的 checked, 当组件触发onTextChange 后,触发父组件的回调函数使得父组件的值得以改变。

三、组件A和无关系组件B之间的通信

如果组件之间没有任何关系,或者组件嵌套的层次比较深,或者,你为了一些组件能够订阅,写入一些信号,不想让两个组件之间插入一个组件,而是让两个组件出于独立的关系。对于时间系统,有两个基本操作:

  1. 订阅: subscribe
  2. 监听: listen

并发送 send / 触发 trigger / 发布 publish / 发送 dispatch 通知那些想要的组件

1. Event Emitter/Target/Dispatcher

特点: 需要一个指定的订阅源

// to subscribe
otherObiect.addEventListener('clickEvent', function() {
  alert('click!');
})
// to dispatch
this.dispatchEvent('clickEvent');

2. Publish / Subscribe

特点: 触发的时候,不需要指定一个特定的源,使用全局对象广播的方式来处理事件

// to subscribe
globalBroadcaster.subcribe('clickEvent', function() {
  alert('cilck!');  
})
// to publish
globalBroadcaster.publish('clickEvent');

这种方案还有一个插件可用, 即 PubSubJs;用法如下:

import Pubsub from 'pubsub-js';
...
// to subscribe
Pubsub.subscribe('EVENT', (msg, param) => {
  console.log(msg, param);
});
// to publish
Pubsub.publish('EVENT', param);

3. Single

特点: 与 Event Emitter/Target/Dispatcher 类似,但是不要使用随机字符串作为事件触发的引用。触发事件的每一个对象都需要一个确切的名字,并且在触发的时候,也必须要指定确切的事件

// to subscribe
otherObject.clicked.add(function() {
  alert('click');
})
// to dispatch
this.clicked.dispatch();

React 团队使用的是:js-signals 它基于 Signals 模式,用起来相当不错。

事件订阅与取消

使用React事件的时候,必须关注以下两个方法:

  1. componentDidMount
  2. componentWillUnmount

在 componentDidMount 事件中,等待组件挂载 mounted 完成,再订阅事件;订阅的事件需要在组件卸载 componentWillUnmount 的时候取消事件的订阅。

因为组件的渲染和销毁是有 React 来控制的,我们不知道怎么引用他们,所以EventEmitter 模式在处理事件的时候用处不大,Pub/Sub 模式就好用些,因为我们不需要知道引用在哪。

ES6策略: yield and js-csp

ES6中有一种传递信息的方式,使用生成函数 generators 和 yield 关键字,用法参考以下例子

import csp from 'js-csp';

function* list() {
  for(var i = 0; i< arguments.length; i++) {
    yield arguments[i];
  }
  return "done";
}
var o = list(1, 2, 3);
var cur = o.next;
while (!cur.done) {
  cur = o.next();
  console.log(cur);
}

结束语

数据在组件中应该以什么样的方式进行传递取决于组件之间存在什么样的关系和当时的业务场景需求,大家应该根据项目合理选择数据处理的方案,这很可能减少你大量的代码量和代码逻辑。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
一个简单的网站访问JS计数器 刷新1次加1次访问
Sep 20 Javascript
jquery ui dialog实现弹窗特效的思路及代码
Aug 03 Javascript
javascript完美拖拽的实现方法
Sep 29 Javascript
js实例属性和原型属性示例详解
Nov 23 Javascript
JavaScript实现删除,移动和复制文件的方法
Aug 05 Javascript
在AngularJS中使用jQuery的zTree插件的方法
Apr 21 Javascript
Ajax+FormData+javascript实现无刷新表单信息提交
Oct 24 Javascript
常见的浏览器Hack技巧整理
Jun 29 Javascript
分析javascript中9 个常见错误阻碍你进步
Sep 18 Javascript
angular.js和vue.js中实现函数去抖示例(debounce)
Jan 18 Javascript
Vue.directive 自定义指令的问题小结
Mar 04 Javascript
node实现的爬虫功能示例
May 04 Javascript
三分钟学会用ES7中的Async/Await进行异步编程
Jun 14 #Javascript
详解React中setState回调函数
Jun 14 #Javascript
JavaScript 判断对象中是否有某属性的常用方法
Jun 14 #Javascript
Vue.js添加组件操作示例
Jun 13 #Javascript
vue 项目打包通过命令修改 vue-router 模式 修改 API 接口前缀
Jun 13 #Javascript
clipboard.js在移动端复制失败的解决方法
Jun 13 #Javascript
ES6与CommonJS中的模块处理的区别
Jun 13 #Javascript
You might like
php下的权限算法的实现
2007/04/28 PHP
php中可能用来加密字符串的函数[base64_encode、urlencode、sha1]
2012/01/16 PHP
php中随机函数mt_rand()与rand()性能对比分析
2014/12/01 PHP
php版微信小店调用api示例代码
2016/11/12 PHP
一个多次搜索+多次传值的解决方案
2007/01/20 Javascript
Prototype PeriodicalExecuter对象 学习
2009/07/19 Javascript
JS 中document.URL 和 windows.location.href 的区别
2009/11/11 Javascript
给jqGrid数据行添加修改和删除操作链接(之一)
2011/11/04 Javascript
js 实现css风格选择器(压缩后2KB)
2012/01/12 Javascript
windows系统下简单nodejs安装及环境配置
2013/01/08 NodeJs
比较不错的JS/JQuery显示或隐藏文本的方法
2014/02/13 Javascript
Jquery中基本选择器用法实例详解
2015/05/18 Javascript
JavaScript如何实现对数字保留两位小数一位自动补零
2015/12/18 Javascript
Jquery uploadify上传插件使用详解
2016/01/13 Javascript
angular2倒计时组件使用详解
2017/01/12 Javascript
Angular 4 依赖注入学习教程之FactoryProvider的使用(四)
2017/06/04 Javascript
JavaScript实现求最大公共子串的方法
2018/02/03 Javascript
Js数组扁平化实现方法代码总汇
2020/11/11 Javascript
python发送邮件示例(支持中文邮件标题)
2014/02/16 Python
Python重新引入被覆盖的自带function
2014/07/16 Python
Python操作MySQL数据库的方法
2018/06/20 Python
Django框架模板的使用方法示例
2019/05/25 Python
解决django服务器重启端口被占用的问题
2019/07/26 Python
python pillow模块使用方法详解
2019/08/30 Python
协程Python 中实现多任务耗资源最小的方式
2020/10/19 Python
英国最大线上综合鞋类商城:Office
2017/12/08 全球购物
Ellos瑞典官网:北欧地区时尚、美容和住宅领域领先的电子商务网站
2019/11/21 全球购物
客户接待方案
2014/02/26 职场文书
党组织公开承诺书
2014/03/29 职场文书
小学生竞选班干部演讲稿(5篇)
2014/09/12 职场文书
2014旅游局党组书记党建工作汇报材料
2014/11/02 职场文书
入党积极分子党支部意见
2015/06/02 职场文书
未婚证明范本
2015/06/15 职场文书
跳高加油稿
2015/07/21 职场文书
班干部竞选演讲稿(精选5篇)
2019/09/24 职场文书
高性能跳频抗干扰宽带自组网电台
2022/02/18 无线电