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 相关文章推荐
用 JSON 处理缓存
Apr 27 Javascript
jquery实现居中弹出层代码
Aug 25 Javascript
jQuery EasyUI API 中文文档 - Dialog对话框
Nov 15 Javascript
jquery select 设置默认选中的示例代码
Feb 07 Javascript
Bootstrap自定义文件上传下载样式
May 26 Javascript
JavaScript中的Array 对象(数组对象)
Jun 02 Javascript
几种tab切换详解
Feb 03 Javascript
详解开发react应用最好用的脚手架 create-react-app
Apr 24 Javascript
详解vue通过NGINX部署在子目录或者二级目录实践
Sep 03 Javascript
浅谈vue限制文本框输入数字的正确姿势
Sep 02 Javascript
JavaScript中变量提升机制示例详解
Dec 27 Javascript
一篇文章告诉你如何实现Vue前端分页和后端分页
Feb 18 Vue.js
三分钟学会用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中$_SERVER的详细参数与说明
2008/07/29 PHP
PHP对象Object的概念 介绍
2012/06/14 PHP
巧用php中的array_filter()函数去掉多维空值的代码分享
2012/09/07 PHP
Laravel日志用法详解
2016/10/09 PHP
详解Yaf框架PHPUnit集成测试方法
2017/12/27 PHP
php实现微信公众平台发红包功能
2018/06/14 PHP
父子窗体间传递JSON格式的数据的代码
2010/12/25 Javascript
Webkit的跨域安全问题说明
2011/09/13 Javascript
js函数排序的实例代码
2013/07/01 Javascript
两种不同的方法实现js对checkbox进行全选和反选
2014/05/13 Javascript
javascript事件冒泡和事件捕获详解
2015/05/26 Javascript
jQuery实现鼠标点击弹出渐变层的方法
2015/07/09 Javascript
Spring MVC中Ajax实现二级联动的简单实例
2016/07/06 Javascript
vue项目中公用footer组件底部位置的适配问题
2018/05/10 Javascript
JavaScript实现京东购物放大镜和选项卡效果的方法分析
2018/07/05 Javascript
使用Python来编写HTTP服务器的超级指南
2016/02/18 Python
详解python中asyncio模块
2018/03/03 Python
Python中str.join()简单用法示例
2018/03/20 Python
pandas 空的dataframe 插入列名的示例
2018/10/30 Python
Python代码实现删除一个list里面重复元素的方法
2019/04/02 Python
在OpenCV里使用特征匹配和单映射变换的代码详解
2019/10/23 Python
vue学习笔记之动态组件和v-once指令简单示例
2020/02/29 Python
SISLEY希思黎官方旗舰店:享誉全球的奢华植物美容品牌
2018/04/25 全球购物
计算机通信专业推荐信
2014/02/22 职场文书
青年教师师德演讲稿
2014/08/26 职场文书
社区个人对照检查材料(群众路线)
2014/09/26 职场文书
教师群众路线教育实践活动学习笔记
2014/11/05 职场文书
少先队辅导员事迹材料
2014/12/24 职场文书
党委工作总结2015
2015/04/27 职场文书
干部作风纪律整顿心得体会
2016/01/23 职场文书
2016大学先进团支部事迹材料
2016/03/01 职场文书
SQLServer2008提示评估期已过解决方案
2021/04/12 SQL Server
使用pandas生成/读取csv文件的方法实例
2021/07/09 Python
源码分析Redis中 set 和 sorted set 的使用方法
2022/03/22 Redis
vue实现书本翻页动画效果实例详解
2022/04/08 Vue.js
Mysql的Table doesn't exist问题及解决
2022/12/24 MySQL