React数据传递之组件内部通信的方法


Posted in Javascript onDecember 31, 2017

1. 概述

脱离初级前端一段时间后会发现,写样式的时间越来越少,处理数据的时间越来越多。处理数据的过程也就是实现业务逻辑的过程,这在项目中无疑是最重要的。

所以学习前端框架,了解完基本语法后,接下来就要学习其如何进行数据传递。

Angular 设计之初的一大亮点就是实现了数据的双向绑定,使用 Vue 一段时间后发现,所谓数据的双向绑定,组件内部唯一的应用场景就是 form 表单(input,textarea,select, radio),而这种场景下的数据双向绑定,即便框架内部没有实现,自己实现起来也非常简单。明白这一点后感觉之前认为 React 没有实现数据双向绑定很 low 的想法很幼稚。

对于 React 的数据传递,涉及两方面的内容:

  1. 组件内部的数据传递,典型的应用场景包括如何实现 form 表单双向数据绑定、如何绑定事件;
  2. 组件间的数据传递。 包括父组件往子组件传递数据、子组件往父组件传递数据以及兄弟组件之间传递数据。

本文先讨论组件内部的数据传递。

2. 组件内部数据传递

React 组件内部通信主要分为两部分:数据展示与事件处理。

2.1 数据展示

组件内部数据的展示和更新都是通过 state 来实现的,如果要使用 state 必须使用 ES6 的 class 定义组件。数据更新在双向数据绑定部分探讨,这部分仅讨论展示初始化数据。

如果你熟悉 Vue,React 的 state 对象相当于 Vue 的 data 对象

下面是一个纯展示数据的示例:

class App extends Component {
 constructor(props) {
 super(props);

 // 初始化 state
 this.state = {
  inputValue: "test",
 };
 }

 render() {
 // 注意,在 react 中,DOM 元素是对象,所以使用‘()'包住 
 return (
  <div className="App">
  <p>{this.state.inputValue}</p>
  </div>
 );
 }
}

在通过 class 定义的 React 组件中,除了生命周期钩子函数, constructor() 和 render() 着两个方法也是自动执行的,先执行 constructor() ,执行 constructor() 的同时也是再为 render() 渲染 DOM 做数据准备。

实际上 constructor() 函数是组件生命周期中调用的第一个函数。

2.2 事件

2.2.1 与 DOM 中事件的异同

在 React 中处理事件和在 DOM 中处理事件类似,有两点不同:

  1. React 中通过驼峰命名法命名事件,而不是全是小写字母;
  2. 在 JSX 中直接传递函数作为事件处理程序,而不是字符串。

第 2 点不同有坑,后面细说

举个例子,HTML中的事件:

<button onclick="activateLasers()">
 Activate Lasers
</button>

React 中的事件:

// 因为 jsx 中'{}'里面代表函数表达式,
// 所以传递给 onClick 的实际是函数 activateLasers 的函数体部分,
// 因此需要指定 this 指向,不然会报错
<button onClick={activateLasers}>
 Activate Lasers
</button>

2.2.2 存在的坑

直接传递 function 作为 event handler 需要指定函数的执行环境,即需要手动绑定 this ,不然会报 this 为 undefined 的错。见下面的例子:

class App extends Component {
 constructor(props) {
 super(props);
 this.state = {
  isToggleOn: true,
 };

 // 手动绑定 this
 this.handleClick = this.handleClick.bind(this);
 }

 handleClick() {
 // 如果不在 constructor() 方法中手动绑定 this,直接将其作为事件处理程序 this 为 undefined
 console.log(this);

 this.setState(prevState => ({
  isToggleOn: !prevState.isToggleOn
 }));
 }

 render() {
 return (
  <div className="App">
  <button onClick={this.handleClick}>
   {this.state.isToggleOn ? "on" : "off"}
  </button>
  </div>
 );
 }
}

2.2.3 为什么会有坑

React 官网 说这个锅要 JS 原生语法来背,其实不尽然,React 实在 JS 语法早已确定的情况下设计了这样的事件系统,如果一定要有人站出来背锅,他们五五分吧。

1, JS原生语法存在的问题

JS语法中有这样的规则:如果将一个函数的函数体(没有 () )赋值给另一个变量,函数体内部的 this 指向可能会发生变化。会不会变化取决于函数和被赋值的变量是否处于同一个作用域(相同的执行环境)中,但实际使用中,将一个函数赋值给相同作用域的变量没有意义,那样的话直接使用那个函数就好,没必要在赋值给另一个变量。

this 指向不发生改变的没有意义的例子(为了方便说明,直接使用 var 操作符):

var fn = function () {
 console.log(this);
};

var a = fn;

fn(); // window
a(); // window
this 指向发生改变的例子:

var fn = function () {
 console.log(this);
};

// 将函数体赋值给一个对象的属性,函数执行时 this 和定义时指向不同
var o = {
 a: fn,
};

fn(); // window
o.a(); // o,即{a:f}

如果想要在将函数体赋值另一个变量的同时把原函数的 this 指向也一块赋值过去,就需要在赋值的过程中进行绑定 this 的操作,如下:

var fn = function () {
 console.log(this);
};

// fn 在赋值的同时将内部的 this 打包一块赋值给了 a
var o = {
 a: fn.bind(this),
};

fn(); // window
o.a(); // window

通常在将函数体赋值给变量的时候为了避免 this 出错,都会进行 绑定执行环境的操作 ,典型的例子是 var bindId = document.getElementById.bind(document)

2, JSX 存在的问题

因为 JSX 中 DOM 元素也是对象,给元素的属性赋值实际是给 DOM 元素对象的属性赋值,见下:

const element = (
 <button onClick={this.handleClick}>click me</button>
);

等同于

const element = {
 type: 'button',
 props: {
 onClick: this.handleClick,
 children: 'click me',
 },
};

这实际就是 将函数体赋值给一个对象的属性,函数执行时 this 和定义时指向不同 的场景,和原生语法相同的是 this 指向发生了改变,不同的是原生 JS 中不管怎样, this 总归是有个指向的,而 JSX 直接 undefined 。

所以说不绑定 this 报 undefined 的错不能全怪 JS 原生语法。

3. 双向数据绑定

通过 state 传递数据加上事件处理程序便能实现数据的双向绑定,其背后的思想是(以 input 为例):初始化时将 state 中预定义的 state a 赋值给 input,当 input 的 value 发生改变时,触发事件处理程序,将改变后的 value 赋值给状态 a ,React 监测到 state 改变时重新调用 render() 方法,即重新渲染组件,达到双向绑定的目的。

class App extends Component {
 constructor(props) {
  super(props);
  this.state = {
   inputValue: "test",
  };
  this.changeInput = this.changeInput.bind(this);
 }

 changeInput(e) {
  // 将改变后的 input 值赋值给 inputValue,通过事件对象 $event.target.value 实现
  this.setState({
   inputValue: e.target.value
  });
 }

 render() {
  // input 改变时触发 changeInput
  return (
   <div className="App">
    <input value={this.state.inputValue} onChange={this.changeInput} />
    <p>{this.state.inputValue}</p>
   </div>
  );
 }
}

这里用到了事件对象,React 的事件对象和 JS 原生事件对象保持一致。

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

Javascript 相关文章推荐
JavaScript 开发规范要求(图文并茂)
Jun 11 Javascript
js加入收藏以及使用Jquery更改透明度
Jan 26 Javascript
Javascript基础教程之switch语句
Jan 18 Javascript
js采用concat和sort将N个数组拼接起来的方法
Jan 21 Javascript
JavaScript sort数组排序方法和自我实现排序方法小结
Jun 06 Javascript
纯原生js实现table表格的增删
Jan 05 Javascript
JS常见算法详解
Feb 28 Javascript
React Native react-navigation 导航使用详解
Dec 01 Javascript
vue.js实现的绑定class操作示例
Jul 06 Javascript
jQuery常见的遍历DOM操作详解
Sep 05 jQuery
浅谈Layui的eleTree树式选择器使用方法
Sep 25 Javascript
如何在Vue项目中添加接口监听遮罩
Jan 25 Vue.js
javascript 通过键名获取键盘的keyCode方法
Dec 31 #Javascript
vue vuex vue-rouert后台项目——权限路由(适合初学)
Dec 29 #Javascript
Angular实现的内置过滤器orderBy排序与模糊查询功能示例
Dec 29 #Javascript
Angular实现的敏感文字自动过滤与提示功能示例
Dec 29 #Javascript
Angular实现点击按钮控制隐藏和显示功能示例
Dec 29 #Javascript
js实现手机web图片左右滑动效果
Dec 29 #Javascript
详解react-router 4.0 下服务器如何配合BrowserRouter
Dec 29 #Javascript
You might like
深入php常用函数的使用汇总
2013/06/08 PHP
destoon各类调用汇总
2014/06/20 PHP
php 使用array函数实现分页
2015/02/13 PHP
关于PHP中interface的用处详解
2020/07/26 PHP
javascript一点特殊用法
2008/05/28 Javascript
js 绑定带参数的事件以及手动触发事件
2010/04/27 Javascript
javascript中定义类的方法详解
2015/02/10 Javascript
Node.js编写组件的三种实现方式
2016/02/25 Javascript
iOS和Android用同一个二维码实现跳转下载链接的方法
2016/09/28 Javascript
Vue数据驱动模拟实现2
2017/01/11 Javascript
ES6 迭代器(Iterator)和 for.of循环使用方法学习(总结)
2018/02/08 Javascript
Bootstrap实现可折叠分组侧边导航菜单
2018/03/07 Javascript
jquery 通过ajax请求获取后台数据显示在表格上的方法
2018/08/08 jQuery
对angular2中的ngfor和ngif指令嵌套实例讲解
2018/09/12 Javascript
基于javascript的拖拽类封装详解
2019/04/19 Javascript
vue 路由守卫(导航守卫)及其具体使用
2020/02/25 Javascript
python中将字典转换成其json字符串
2014/07/16 Python
Python二维码生成库qrcode安装和使用示例
2014/12/16 Python
Python中if __name__ == '__main__'作用解析
2015/06/29 Python
Python中MySQLdb和torndb模块对MySQL的断连问题处理
2015/11/09 Python
Python线程条件变量Condition原理解析
2020/01/20 Python
Python闭包与装饰器原理及实例解析
2020/04/30 Python
CSS3 实现穿梭星空动画
2020/11/13 HTML / CSS
深入浅析HTML5中的article和section的区别
2018/05/15 HTML / CSS
莫斯科绝对前卫最秘密的商店:SVMoscow
2017/10/23 全球购物
加拿大领先的冒险和户外零售商:Atmosphere
2017/12/19 全球购物
是否可以从一个static方法内部发出对非static方法的调用?
2014/08/18 面试题
大学生创业计划书的格式要求
2013/12/29 职场文书
西安交大自主招生自荐信
2014/01/27 职场文书
档案保密承诺书
2014/06/03 职场文书
2014年变电站工作总结
2014/12/19 职场文书
商业计划书范文
2019/04/24 职场文书
前端学习——JavaScript原生实现购物车案例
2021/03/31 Javascript
七个Python必备的GUI库
2021/04/27 Python
sass 常用备忘案例详解
2021/09/15 HTML / CSS
CSS实现九宫格布局(自适应)的示例代码
2022/02/12 HTML / CSS