React学习—React漫谈

事件系统

React基于Virtual DOM实现了一个SyntheticEvent(合成事件)层,我们定义的处理器会接收一个SyntheticEvent对象的实例,它完全符合W3C标准,不会存在任何IE的兼容性问题。并且与原生的浏览器事件一样拥有同样的接口,同样支持事件的冒泡机制,我门可以使用stopPropagation()和preventDefault()来中断它。如果需要访问原生事件对象,可以使用nativeEvent属性。

合成事件的绑定方式

React事件的绑定方式与原生的HTML事件监听器属性很相似。

合成事件的实现机制

在React底层,主要对合成事件做了两件事:事件委派和自动绑定。

1.事件委派

React不会把事件处理函数直接绑定到真实的节点上,而是把所有事件绑定到结构的最外层,使用一个统一的事件监听器,这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时,只是在这个统一的事件监听器上插入或删除一些对象;当事件发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。(实现原理:对最外层的容器进行绑定,依赖事件的冒泡机制完成委派。)这样简化了事件处理和回收机制,效率也有很大提升。

2.自动绑定

在React组件中,每个方法的上下文都会指向该组件的实例,即自动绑定this为当前组件。而且React还会对这种引用进行缓存。在使用ES6 classes或者纯函数时,这种自动绑定就不复存在了,我们需要手动实现this的绑定。
我们来看几种绑定方法
bind方法

class App extends Component {
  constuctor() {
    super(props)
    this.handleClick = this.handleClick.bind(this)
  }
}

箭头函数可以自动绑定此函数的作用域的this

class App extends Component {
  handleClick= () => {}
}

在React中使用原生事件

class NativeEventDemo extends Component {
  componentDidMount() {
    this.refs.button.addEventListener('click', this.handleClick)
  }
  componentWillUnmout() {
    this.refs.button.removeEventListener('click', this.handleClick)
  }
}

对比React合成事件与JavaScript原生事件

1.事件传播与阻止事件传播

浏览器原生DOM事件的传播可以分为3个阶段:事件捕获阶段、目标对象本身的事件处理程序调用、事件冒泡。可以将e.addEventListener的第三个参数设置为true时,为元素e注册捕获事件处理程序。事件捕获在IE9以下无法使用。事件捕获在应用程序开发中意义不大,React在合成事件中并没有实现事件捕获,仅仅支持了事件冒泡机制。

阻止原生事件传播需要使用e.stopPropagation,不过对于不支持该方法的浏览器(IE9以下)只能使用e.cancelBubble = true来阻止。而在React合成事件中,只需要使用stopPropagation()即可。阻止React事件冒泡的行为只能用于React合成事件系统中,且没有办法阻止原生事件的冒泡。反之,原生事件阻止冒泡,可以阻止React合成事件的传播。

2.事件类型

React合成事件的事件类型是JavaScript原生事件类型的一个子集。它仅仅实现了DOM Level3的事件接口,并且统一了浏览器的兼容问题。有些事件React没有实现,或者受某些限制没办法去实现,如window的resize事件。

3.事件绑定方式

受到DOM标准影响,浏览器绑定原生事件的方式有很多种。React合成事件的绑定方式则简单很多

4.事件对象

在React合成事件系统中,不存在兼容性问题,可以得到一个合成事件对象。

表单

在React中,一切数据都是状态,当然也包括表单数据。接下来我们讲讲React是如何处理表单的。

应用表单组件

html表单中的所有组件在React的JSX都有实现,只是它们在用法上有些区别,有些是JSX语法上的,有些则是由于React对状态处理上导致的一些区别。

1.文本框

import React, { Component } from 'react';

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

    this.state = {
      inputValue: '',
      textareaValue: ''
    }
  }

  handleInputChange = (e) => {
    this.setState({
      inputValue: e.target.value
    });
  }

  handleTextareaChange = (e) => {
    this.setState({
      textareaValue: e.target.value
    })
  }

  render() {
    const { inputValue, textareaValue } = this.state;
    return (
      

单行输入框:

多行输入框: