资讯专栏INFORMATION COLUMN

5、React组件事件详解

Ververica / 2321人阅读

摘要:组件事件响应在构建虚拟的同时,还构建了自己的事件系统且所有事件对象和规范保持一致。的事件系统和浏览器事件系统相比,主要增加了两个特性事件代理和事件自动绑定。

React组件事件响应

React在构建虚拟DOM的同时,还构建了自己的事件系统;且所有事件对象和W3C规范
保持一致。

React的事件系统和浏览器事件系统相比,主要增加了两个特性:事件代理、和事件自动绑定

1、事件代理

区别于浏览器事件处理方式,React并未将事件处理函数与对应的DOM节点直接关联,而是在顶层使用
了一个全局事件监听器监听所有的事件;

React会在内部维护一个映射表记录事件与组件事件处理函数的对应关系;

当某个事件触发时,React根据这个内部映射表将事件分派给指定的事件处理函数;

当映射表中没有事件处理函数时,React不做任何操作;

当一个组件安装或者卸载时,相应的事件处理函数会自动被添加到事件监听器的内部映射表中或从表中删除。

2、事件自动绑定

在JavaScript中创建回调函数时,一般要将方法绑定到特定的实例,以保证this的正确性;

2.在React中,每个事件处理回调函数都会自动绑定到组件实例(使用ES6语法创建的例外);

注意:事件的回调函数被绑定在React组件上,而不是原始的元素上,即事件回调函数中的
this所指的是组件实例而不是DOM元素;

了解更多React中的thisReact组件中的this。

3、合成事件
与浏览器事件处理稍微有不同的是,React中的事件处理程序所接收的事件参数是被称为“合成事件(SyntheticEvent)”的实例。

合成事件是对浏览器原生事件跨浏览器的封装,并与浏览器原生事件有着同样的接口,如stopPropagation(),preventDefault()等,并且
这些接口是跨浏览器兼容的。

如果需要使用浏览器原生事件,可以通过合成事件的nativeEvent属性获取

React合成事件原理

使用JSX,在React中绑定事件:

React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行

每个合成事件有以下通用属性:

    - boolean bubbles
    - boolean cancelable
    - DOMEventTarget currentTarget
    - boolean defaultPrevented
    - number eventPhase
    - boolean isTrusted
    - DOMEvent nativeEvent
    - void preventDefault()
    - boolean isDefaultPrevented()
    - void stopPropagation()
    - boolean isPropagationStopped()
    - DOMEventTarget target
    - number timeStamp
    - string type

注意:现版本React在事件处理程序通过中返回false停止传播,已不可用;
取而代之的是需要手动调用e.stopPropagation()或e.preventDefalult().

React支持的常用事件 1、剪贴板事件

onCopy onCut onPaste

2、键盘事件

onKeyDown onKeyPress onKeyUp

3、焦点事件

onFocus onBlur

这些焦点事件工作在 React DOM 中所有的元素上 ,不仅是表单元素。

4、表单事件

onChange onInput onSubmit

onChange事件经过React改良,内容改变时即可实时触发;而原生的需内容改变且失去焦点后触发才触发。

5、鼠标事件
onClick onContextMenu onDoubleClick 

onDrag onDragEnd onDragEnter onDragExit

onDragLeave onDragOver onDragStart onDrop

onMouseDown onMouseEnter onMouseLeave

onMouseMove onMouseOut onMouseOver onMouseUp 

onMouseEnter 和 onMouseLeave 事件从离开的元素传播到正在进入的元素,而不是普通的冒泡,并且没有捕获阶段;只有在鼠标指针穿过被选元素时,才会触发。

onMouseOut onMouseOver事件:不论鼠标指针穿过被选元素或其子元素,都会触发。

6、选择事件

onSelect

7、触摸事件

onTouchCancel onTouchEnd onTouchMove onTouchStart

8、UI事件

onScroll

9、滚轮事件

onWheel

10、图像事件

onLoad onError

11、动画事件

onAnimationStart onAnimationEnd onAnimationIteration

12、其他事件

onToggle

在React中使用原生事件

由于原生事件需要绑定在真实DOM上,所以一般是在 componentDidMount阶段/ref的函数执行阶段进行绑定操作,在componentWillUnmount 阶段进行解绑操作以避免内存泄漏。

import React,{Component} from "react";
import ReactDOM from "react-dom"

class ReactEvent extends Component {

    componentDidMount() {
        //获取当前真实DOM元素
        const thisDOM = ReactDOM.findDOMNode(this);
        thisDOM.addEventListener("click",this.onDOMClick,false);

    }

    componentWillUnmount() {
        //卸载时解绑事件,防止内存泄漏
        const thisDOM = ReactDOM.findDOMNode(this);
        thisDOM.removeEventListener("click",this.removeDOMClick);
    }

    onDOMClick(e){
        console.log(e)
    }

    render(){
        return(
            
单击原始事件触发
) } } export default ReactEvent
合成事件和原生事件混合使用

响应顺序

import React,{Component} from "react";
import ReactDOM from "react-dom"

class ReactEvent extends Component {
    constructor(props){
        super(props)
        this.state = {
            value: ""
        }
        this.onReactClick = this.onReactClick.bind(this)
    }
    componentDidMount() {
        //获取当前真实DOM元素
        const thisDOM = ReactDOM.findDOMNode(this);
        thisDOM.addEventListener("click",this.onDOMClick,false);

    }

    componentWillUnmount() {
        //卸载时解绑事件,防止内存泄漏
        const thisDOM = ReactDOM.findDOMNode(this);
        thisDOM.removeEventListener("click",this.removeDOMClick);
    }

    onDOMClick(e){
        console.log("原生事件绑定事件触发")
    }

    onReactClick(e){
        console.log("React合成事件绑定事件触发")
    }

    render(){
        return(
            
单击事件触发
) } } export default ReactEvent

首先DOM事件监听器被执行,然后事件继续冒泡至document,合成事件监听器再被执行。

即,最终控制台输出为:

原生事件绑定事件触发

合成事件绑定事件触发

阻止冒泡

如果在onDOMClick中调用e.stopPropagtion()

    onDOMClick(e){
        e.stopPropagation()
        console.log("原生事件绑定事件触发")
    }

由于DOM事件被阻止冒泡了,无法到达document,所以合成事件自然不会被触发,控制台输出就变成了:

原生事件绑定事件触发

再测试个复杂的例子

import React,{Component} from "react";
import ReactDOM from "react-dom"

class ReactEvent extends Component {
    constructor(props){
        super(props)
        this.state = {
            value: ""
        }
        this.onReactClick = this.onReactClick.bind(this)
        this.onReactChildClick = this.onReactChildClick.bind(this)
    }
    componentDidMount() {
        //获取当前真实DOM元素
        const thisDOM = ReactDOM.findDOMNode(this);
        thisDOM.addEventListener("click",this.onDOMClick,false);
        //获取子元素并绑定事件
        const thisDOMChild = thisDOM.querySelector(".child");
        thisDOMChild.addEventListener("click",this.onDOMChildClick,false);

    }

    onDOMClick(e){

        console.log("父组件原生事件绑定事件触发")
    }

    onReactClick(e){
        console.log("父组件React合成事件绑定事件触发")
    }

    onDOMChildClick(e){
        e.stopPropagation()
        console.log("子元素原生事件绑定事件触发")
    }

    onReactChildClick(e){

        console.log("子元素React合成事件绑定事件触发")
    }

    render(){
        return(
            
父元素单击事件触发
) } } export default ReactEvent

通过设置原生事件绑定为冒泡阶段调用,且每次测试单击子元素按钮:

在子元素原生事件程序中阻止事件传播,则打印出:

子元素原生事件绑定事件触发;

在父元素元素事件程序中阻止事件传播,则打印出:

子元素原生事件绑定事件触发

父组件原生事件绑定事件触发

在子元素React合成事件onClick中阻止事件传播,则打印出:

子元素原生事件绑定事件触发

父组件原生事件绑定事件触发

子元素React合成事件绑定事件触发

在父元素React合成事件onClick中阻止事件传播,则打印出:

子元素原生事件绑定事件触发

父组件原生事件绑定事件触发

子元素React合成事件绑定事件触发

父组件React合成事件绑定事件触发

可以看到若不阻止事件传播每次(单击子元素)事件触发流程是:

Document->子元素(原生事件触发)->父元素(原生事件)->回到Document->React子元素合成事件监听器触发 ->React父元素合成事件监听器触发

其实,React合成事件封装的stopPropagtion函数在调用时给自己加了个isPropagationStopped的标记位来确定后续监听器是否执行。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/93282.html

相关文章

  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    jsbintask 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    ?xiaoxiao, 评论0 收藏0
  • 前端文档收集

    摘要:系列种优化页面加载速度的方法随笔分类中个最重要的技术点常用整理网页性能管理详解离线缓存简介系列编写高性能有趣的原生数组函数数据访问性能优化方案实现的大排序算法一怪对象常用方法函数收集数组的操作面向对象和原型继承中关键词的优雅解释浅谈系列 H5系列 10种优化页面加载速度的方法 随笔分类 - HTML5 HTML5中40个最重要的技术点 常用meta整理 网页性能管理详解 HTML5 ...

    muddyway 评论0 收藏0
  • react-navigation使用详解

    摘要:导航组件使用详解注意了,如果有小伙伴们发现运行作者提供的示例项目报如下的错误,可能是大家使用了命令导致的,解决这个错误的办法就是将删除,然后重新使用命令来安装,最后使用来起服务,应该就不报错了。 react-navigation导航组件使用详解 注意了,如果有小伙伴们发现运行作者提供的react-navigation示例项目报如下的错误,可能是大家使用了 yarn install 命...

    stonezhu 评论0 收藏0
  • React组件调用子组件中的方法实例详解

      本文主要讲述ref 的应用仅为父组件调用子组件场景下的应用方式1  Class组件  1. 自定义事件  Parent.js  importReact,{Component}from'react';   importChildfrom'./Child';   classParentextendsComponent{   componentDidMount(){ ...

    3403771864 评论0 收藏0

发表评论

0条评论

Ververica

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<