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 相关文章推荐
脚本安需导入(装载)的三种模式的对比
Jun 24 Javascript
js no-repeat写法 背景不重复
Mar 18 Javascript
javascript 数据类型转换(parseInt,parseFloat)
Jul 20 Javascript
让JavaScript拥有类似Lambda表达式编程能力的方法
Sep 12 Javascript
EXTjs4.0的store的findRecord的BUG演示代码
Jun 08 Javascript
jquery验证邮箱格式并显示提交按钮
Nov 07 Javascript
z-blog SyntaxHighlighter 长代码无法换行解决办法(基于jquery)
Nov 18 Javascript
jQuery使用$.each遍历json数组的简单实现方法
Apr 18 Javascript
JavaScript 中定义函数用 var foo = function () {} 和 function foo()区别介绍
Mar 01 Javascript
Vue 路由 过渡动效 数据获取方法
Jul 31 Javascript
JS 获取文件后缀,判断文件类型(比如是否为图片格式)
May 09 Javascript
原生JavaScript实现五子棋游戏
Nov 09 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正确配置mysql(apache环境)
2011/08/28 PHP
PHP APC的安装与使用详解
2013/06/13 PHP
PHP中的插件机制原理和实例
2014/07/08 PHP
PHP 断点续传实例详解
2017/11/11 PHP
一段多浏览器的&quot;复制到剪贴板&quot;javascript代码
2007/03/27 Javascript
jquery判断字符输入个数(数字英文长度记为1,中文记为2,超过长度自动截取)
2010/10/15 Javascript
jQuery Animation实现CSS3动画示例介绍
2013/08/14 Javascript
jquery批量设置属性readonly和disabled的方法
2014/01/24 Javascript
javascript获取和判断浏览器窗口、屏幕、网页的高度、宽度等
2014/05/08 Javascript
node.js WEB开发中图片验证码的实现方法
2014/06/03 Javascript
jQuery中parents()方法用法实例
2015/01/07 Javascript
javascript实现youku的视频代码自适应宽度
2015/05/25 Javascript
Angular工具方法学习
2016/12/26 Javascript
js前端日历控件(悬浮、拖拽、自由变形)
2017/03/02 Javascript
如何正确理解javascript的模块化
2017/03/02 Javascript
vue修改vue项目运行端口号的方法
2017/08/04 Javascript
10个最优秀的Node.js MVC框架
2017/08/24 Javascript
原生js实现二级联动菜单
2019/11/27 Javascript
JS绘图Flot如何实现动态可刷新曲线图
2020/10/16 Javascript
[15:20]DOTA2亚洲邀请赛总决赛开幕式表演:羽泉献唱
2017/04/05 DOTA
python二叉树的实现实例
2013/11/21 Python
Python sys.argv用法实例
2015/05/28 Python
详解Python中的相对导入和绝对导入
2017/01/06 Python
Mac中升级Python2.7到Python3.5步骤详解
2017/04/27 Python
浅谈numpy数组的几种排序方式
2017/12/15 Python
python+pandas+时间、日期以及时间序列处理方法
2018/07/10 Python
Python+appium框架原生代码实现App自动化测试详解
2020/03/06 Python
英国著名的茶叶品牌:Whittard of Chelsea
2016/09/22 全球购物
韩国美国时尚服装和美容在线全球市场:KOODING
2018/11/07 全球购物
OLEDBConnection和SQLConnection有什么区别
2013/05/31 面试题
机电一体化专业推荐信
2013/12/03 职场文书
新郎新娘婚礼答谢词
2014/01/11 职场文书
计算机网络及管理学专业求职信
2014/06/05 职场文书
我的中国梦演讲稿500字
2014/08/19 职场文书
2014年学习委员工作总结
2014/11/14 职场文书
《追风筝的人》:人心中的成见是座大山,但请不忘初心
2019/11/15 职场文书