当前位置: 首页 > news >正文

5、React组件事件详解

React组件事件响应

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

1、事件代理

  1. 区别于浏览器事件处理方式,React并未将事件处理函数与对应的DOM节点直接关联,而是在顶层使用
    了一个全局事件监听器监听所有的事件;
  2. React会在内部维护一个映射表记录事件与组件事件处理函数的对应关系;
  3. 当某个事件触发时,React根据这个内部映射表将事件分派给指定的事件处理函数;
  4. 当映射表中没有事件处理函数时,React不做任何操作;
  5. 当一个组件安装或者卸载时,相应的事件处理函数会自动被添加到事件监听器的内部映射表中或从表中删除。

2、事件自动绑定

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

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

  1. 注意:事件的回调函数被绑定在React组件上,而不是原始的元素上,即事件回调函数中的
    this所指的是组件实例而不是DOM元素;
  2. 了解更多React中的thisReact组件中的this。

3、合成事件

与浏览器事件处理稍微有不同的是,React中的事件处理程序所接收的事件参数是被称为 “合成事件(SyntheticEvent)”的实例。
  1. 合成事件是对浏览器原生事件跨浏览器的封装,并与浏览器原生事件有着同样的接口,如stopPropagation(),preventDefault()等,并且
    这些接口是跨浏览器兼容的。
  2. 如果需要使用浏览器原生事件,可以通过合成事件的nativeEvent属性获取
  3. React合成事件原理

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

<button onClick={this.onClick}>
    单击触发react事件
</button>
  • React并不是将click事件绑在该div的真实DOM上,而是在document处监听所有支持的事件,当事件发生并冒泡至document处时,React将事件内容封装并交由真正的处理函数运行

图片描述

  1. 每个合成事件有以下通用属性:
    - 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(
            <div>
                单击原始事件触发
            </div>
        )
    }
}
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(
            <div onClick={this.onReactClick}>
                单击事件触发
            </div>
        )
    }
}
export default ReactEvent
  • 首先DOM事件监听器被执行,然后事件继续冒泡至document,合成事件监听器再被执行。
  • 即,最终控制台输出为:

    1. 原生事件绑定事件触发
    2. 合成事件绑定事件触发
  • 阻止冒泡
  1. 如果在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(
            <div onClick={this.onReactClick}>                
                父元素单击事件触发
                <button className="child" onClick={this.onReactChildClick}>子元素单击事件触发</button>
            </div>
        )
    }
}
export default ReactEvent
  • 通过设置原生事件绑定为冒泡阶段调用,且每次测试单击子元素按钮:

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

      • 子元素原生事件绑定事件触发;
    2. 在父元素元素事件程序中阻止事件传播,则打印出:

      • 子元素原生事件绑定事件触发
      • 父组件原生事件绑定事件触发
    3. 在子元素React合成事件onClick中阻止事件传播,则打印出:

      • 子元素原生事件绑定事件触发
      • 父组件原生事件绑定事件触发
      • 子元素React合成事件绑定事件触发
    4. 在父元素React合成事件onClick中阻止事件传播,则打印出:

      • 子元素原生事件绑定事件触发
      • 父组件原生事件绑定事件触发
      • 子元素React合成事件绑定事件触发
      • 父组件React合成事件绑定事件触发
  • 可以看到若不阻止事件传播每次(单击子元素)事件触发流程是:

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

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

相关文章:

  • Linksys WRT54G 路由器溢出漏洞分析—— 运行环境修复
  • 也谈链路劫持
  • kbasesrv篡改主页分析
  • 陈皓:如何测试洗牌程序
  • Object转Integer,String
  • 解析 Webpack中import、require、按需加载的执行过程
  • Android使用TextView,设置onClick属性无效解决办法
  • Python:SMOTE算法——样本不均衡时候生成新样本的算法
  • 前端之css
  • Quicklz压缩算法
  • CMD命令
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • mysqli简介
  • hibernate多对多
  • QASymphony发布新的中心化自动测试及行为驱动测试产品
  • SegmentFault for Android 3.0 发布
  • 4. 路由到控制器 - Laravel从零开始教程
  • export和import的用法总结
  • Fabric架构演变之路
  • JavaScript设计模式与开发实践系列之策略模式
  • mockjs让前端开发独立于后端
  • open-falcon 开发笔记(一):从零开始搭建虚拟服务器和监测环境
  • Redis 懒删除(lazy free)简史
  • Redis字符串类型内部编码剖析
  • vuex 学习笔记 01
  • vue总结
  • Windows Containers 大冒险: 容器网络
  • Zsh 开发指南(第十四篇 文件读写)
  • 从零开始的webpack生活-0x009:FilesLoader装载文件
  • 理清楚Vue的结构
  • 驱动程序原理
  • 什么软件可以提取视频中的音频制作成手机铃声
  • 数据可视化之 Sankey 桑基图的实现
  • 《码出高效》学习笔记与书中错误记录
  • Java数据解析之JSON
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • #大学#套接字
  • #控制台大学课堂点名问题_课堂随机点名
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • ( 用例图)定义了系统的功能需求,它是从系统的外部看系统功能,并不描述系统内部对功能的具体实现
  • (C语言)strcpy与strcpy详解,与模拟实现
  • (done) ROC曲线 和 AUC值 分别是什么?
  • (二)Pytorch快速搭建神经网络模型实现气温预测回归(代码+详细注解)
  • (翻译)terry crowley: 写给程序员
  • (附源码)ssm教师工作量核算统计系统 毕业设计 162307
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (九)c52学习之旅-定时器
  • (三) prometheus + grafana + alertmanager 配置Redis监控
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (转)关于多人操作数据的处理策略
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET Core中的去虚
  • .NET Micro Framework 4.2 beta 源码探析
  • .Net Remoting常用部署结构
  • .net web项目 调用webService