react 事件处理

news2024/11/17 13:37:59

概述

Web应用中,事件处理是重要的一环,事件处理将用户的操作行为转换为相应的逻辑执行或界面更新。在React中,处理事件响应的方式有多种,本文将详细介绍每一种处理方式的用法、使用场景和优缺点。

如果原生DOM有一个监听事件,我们可以如何操作?

  • 获取DOM元素,添加监听事件
  • 在HTML中直接进行onClick绑定

使用匿名函数

点击Button的事件响应函数是一个匿名函数,这应该是最常见的处理事件响应的方式了。这种方式的好处是,简单直接。哪里需要处理事件响应,就在哪里定义一个匿名函数处理。代码1中的匿名函数使用的是箭头函数,我们也可以不使用箭头函数:

虽然代码2的运行效果和代码1相同,但实际项目中很少见到代码2的这种写法。这是因为箭头函数解决了this绑定的问题,可以将函数体内的this绑定到当前对象,而不是运行时调用函数的对象。如果响应函数中需要使用this.state,那么代码2就无法正常运行了。所以项目中一般直接使用箭头函数定义的匿名函数作为事件响应。

使用匿名函数的缺点是:当事件响应逻辑比较复杂时,匿名函数的代码量会很大,会导致render函数变得臃肿,不容易直观地看出组件最终渲染出的元素结构。另外,每次render方法调用时,都会重新创建一个匿名函数对象,带来额外的性能开销,当组件的层级越低时,这种开销就越大,因为任何一个上层组件的变化都可能会触发这个组件的render方法。当然,在大多数情况下,这点性能损失是可以不必在意的。

使用组件方法

代码如下:

点击Button的事件响应函数是组件的方法:handleClick。这种方式的好处是:每次render方法的调用,不会重新创建一个新的事件响应函数,没有额外的性能损失。但是,使用这种方式要在构造函数中为作为事件响应的方法(handleClick),手动绑定this: this.handleClick = this.handleClick.bind(this),这是因为ES6 语法的缘故,ES6 Class 的方法默认不会把this绑定到当前的实例对象上,需要我们手动绑定。每次都手动绑定this是不是有点繁琐?好吧,让我们来看下一种方式。

使用属性初始化语法

使用ES7的属性初始化语法( property initializers ),代码可以这样写:

这样一来,再也不用手动绑定this了。但是你需要知道,这个特性还处于试验阶段,默认是不支持的。如果你是使用官方脚手架create-react-app 创建的应用,那么这个特性是默认支持的。你也可以自行在项目中引入babel的transform-class-properties插件获取这个特性支持。

事件响应函数的传参问题

事件响应函数默认是会被传入一个事件对象Event作为参数的。如果想传入其他参数给响应函数应该怎么办呢?

使用第一种方式的话很简单,直接使用新参数:

onClick的响应函数中,方法体内可以直接使用新的参数item。

使用第二种方式的话,可以把绑定this的操作延迟到render中,在绑定this的同时,绑定额外的参数:

使用第三种方式,解决方案和第二种基本一致:

不过这种方式就有点鸡肋了,因为虽然你不需要通过bind函数绑定this,但仍然要使用bind函数来绑定其他参数。

关于事件响应函数,还有一个地方需要注意。不管你在响应函数中有没有显式的声明事件参数Event,React都会把事件Event作为参数传递给响应函数,且参数Event的位置总是在其他自定义参数的后面。例如,在代码6和代码7中,handleClick的参数中虽然没有声明Event参数,但你依然可以通过arguments[1]获取到事件Event对象。

总结一下,三种事件处理的方式,第一种有额外的性能损失;第二种需要手动绑定this,代码量增多;第三种用到了ES7的特性,目前并非默认支持,需要Babel插件的支持,但是写法最为简洁,也不需要手动绑定this。一般推荐使用第二种和第三种方式。

这里呢,只是对事件处理做了一个简要的概述,下面我们将详细讲解有关事件操作的方法

注册事件

  • React注册事件与DOM的事件语法非常像
  • 语法on+事件名={事件处理程序} 比如onClick={this.handleClick}
  • 注意:React事件采用驼峰命名法,比如onMouseEnter, onClick

注意事项:

  1. 在 react 中时间的命名采用的是小驼峰形式(例如:onClick),而不是纯小写
  2. 我们需要通过{}传入一个事件处理函数,这个函数会在事件发生时被执行

函数式组件中绑定事件

import React from "react";

// 方式1
// function handlerClick() {
//   console.log(1);
// }

// 方式2:推荐
// const handlerClick = () => {
//   console.log(1);
// }

export default function funHello() {
  // function handlerClick() {
  //   console.log(1);
  // }

  const handlerClick = () => {
    console.log(1);
  };

  return (
    <div>
      <button onClick={handlerClick}>点击</button>
    </div>
  );
}

类组件中绑定事件

import React, { Component } from 'react'

export default class classHello extends Component {
  constructor(props){
    super(props)
    this.state = {
      message: 'hello'
    }
  }

  // 方式1:利用箭头函数--推荐
  handlerMsg = () => {
    this.setState({
      message: 'hello react'
    })
  }

  // 方式2:利用bind等方式改变this指向
  // handlerMsg() {
  //   console.log(this);
  // }

  render() {
    const { message } = this.state
    return (
      <div>
        <p>{message}</p>
        <button onClick={this.handlerMsg}>更改</button>
        {/* <button onClick={this.handlerMsg.bind(this)}>更改</button> */}
      </div>
    )
  }
}

事件对象

  • 可以通过事件处理程序的参数获取到事件对象
function handleClick(e) {
    e.preventDefault()
    console.log('事件对象', e)
}
<a onClick={this.handleClick}>点我,不会跳转页面</a>


// 或者
handleClick = (e) => {
    e.preventDefault()
    console.log('事件对象', e)
}
<a onClick={this.handleClick}>点我,不会跳转页面</a>


handlerMsg(e) {
    console.log(e, this);
  }
<button onClick={(e) => this.handlerMsg(e)}>更改</button>


handlerMsg(e, name, age) {
    console.log(e, this, name, age);
  }
<button onClick={(e) => this.handlerMsg(e, 'zs', 15)}>更改</button>

this指向问题

在jsx中事件处理函数执行时,会导致this丢失,我们可以用bind修改回来

事件处理程序中的this指向的是undefined

render方法中的this指向的而是当前react组件。只有事件处理程序中的this有问题

  • 在类中直接定义一个函数,并且将这个函数绑定到元素的onClick事件上,当前这个函数的this指向的是谁呢?---undefined
  • 默认情况下是undefined
  • 比如 <button onClick={this.handlerMsg}>更改</button>,这里的 this.handlerMsg 当点击之后并不是直接去调用函数,而是进行一个指向操作,react代码在进行编译的时候通过bable,默认会将代码转换成严格模式,在严格模式下的this指向是undefined,因此默认情况下也就是undefined
  • 我们在绑定的函数中,可能想要使用当前对象,比如执行 this.setState 函数,就必须拿到当前对象的this
  • 此时我们就需要在传入函数时,给这个函数直接绑定this
  • 我们可以利用 bind 将其修改回来,比如:<button onClick={this.changeText.bind(this)}>改变文本</button>
  • 当然也可以利用箭头函数来解决

import React, { Component } from 'react'

export default class classHello extends Component {
  constructor(props){
    super(props)
    this.state = {
      message: 'hello'
    }
  }

  // 方式1:利用箭头函数--推荐
  handlerMsg = () => {
    this.setState({
      message: 'hello react'
    })
  }

  // 方式2:利用bind等方式改变this指向
  // handlerMsg() {
  //	this.setState({
  //   	message: 'hello react'
  //  })
  // }
  
  // 方式3:利用声明式
  //function handlerMsg() {
  //  this.setState({
  //    message: 'hello react'
  //  })
 // }

  render() {
    return (
      <div>
        <p>{this.state.message}</p>
        <button onClick={this.handlerMsg}>更改</button>
        {/* <button onClick={this.handlerMsg.bind(this)}>更改</button> */}
      </div>
    )
  }
}

this指向问题解决方案

方案1:箭头函数
方案2:bind修改this指向
方案3:类实例方法

使用箭头函数
import React, { Component } from 'react'

export default class ClickClass extends Component {
    constructor() {
        super();
        this.msg = '消息';
    }
  
  	// 箭头函数
    click = () => {
        console.log('类组件点击事件');
        console.log(this.msg);
    }
  render() {
    return (
      <div>
        <div onClick={this.click}>类组件点击事件</div>
      </div>
    )
  }
}

箭头函数的特点:自身没有this,访问的是外部的this

缺点:会把大量的js处理逻辑放到JSX中,将来不容易维护

直接传入箭头函数

推荐

更有利于传参

class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  
  handleClick() {
    console.log(this.state.msg)
  }
  
  render() {
    return (
      <div>
        <button onClick={() => {this.handleClick()}}>点我</button>
      </div>
    )
  }
}

缺点:把大量的js逻辑写在了JSX结构中,不好维护

使用bind

使用 bind 指明 this 指向

class App extends React.Component {
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick.bind(this)}>点我</button>
      </div>
    )
  }
}

或者(在构造器中使用bind)

class App extends React.Component {
  constructor() {
  	super()
    this.handleClick = this.handleClick.bind(this)
    this.msg = '消息';
  }
  state = {
    msg: 'hello react'
  }
  handleClick() {
    console.log(this.state.msg)
    console.log(this.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

class实例方法
class App extends React.Component {
  state = {
    msg: 'hello react'
  }

  handleClick = () => {
    console.log(this.state.msg)
  }
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>点我</button>
      </div>
    )
  }
}

注意:这个语法是试验性的语法,但是有babel的转义,所以没有任何问题

组件中如何给函数传递参数

箭头函数包裹进行参数传递

推荐

绑定事件函数时传递参数,可以直接绑定一个箭头函数,然后在箭头函数里面执行逻辑函数

import React, { Component } from 'react'

export default class ClickClass extends Component {
    constructor() {
        super();
        this.msg = '消息';
    }


    click = (id) => {
        console.log('类组件点击事件');
        console.log(id);
    }

  render() {
    return (
      <div>
        <div onClick={() => this.click(10)}>类组件点击事件</div>
      </div>
    )
  }
}

获取event:

import React, { PureComponent } from 'react'

export default class demoClassComponent extends PureComponent {
  constructor() {
    super()
    this.state = {
      title: 'hello world',
    }
  }

  changeTitle(event, title, title1) {
    this.setState({
      title: title,
    })
    console.log(title)
    console.log(event)
    console.log(title1)
  }

  render() {
    const { title } = this.state

    return (
      <div>
        <p>{title}</p>
        <button
          onClick={(event) => this.changeTitle(event, '哈哈哈', '嘻嘻嘻')}
        >
          更改
        </button>
      </div>
    )
  }
}

使用.bind传递参数

import React, { Component } from 'react'
export default class ClickClass extends Component {
    constructor() {
        super();
        this.msg = '消息';
    }
    click = (id) => {
        console.log('类组件点击事件');
        console.log(id);
    }
  render() {
    return (
      <div>
        <div onClick={this.click.bind(this, 10)}>类组件点击事件</div>
      </div>
    )
  }
}

Tab案例

import React, { Component } from 'react'
export default class ClickClass extends Component {
    constructor() {
        super();
        this.state = {
          tabs: ['tab1', 'tab2', 'tab3'],
          currentIndex: 0
        };
    }
  
    itemClick = (index) => {
        this.setState({
          currentIndex: index
        })
    }
  render() {
    const {tabs, currentIndex} = this.state;
    return (
      <div>
        <ul>
        	{
            tabs.map((item, index) => {
              return (
              	<li className= {currentIndex === index ? 'active' : ''} key="item" onClick={() => this.itemClick(index)}>{item}</li>
              )
            })
          }
        </ul>
      </div>
    )
  }
}

传递多个参数:

import React, { PureComponent } from 'react'

export default class demoClassComponent extends PureComponent {
  constructor() {
    super()

    this.state = {
      title: '哈哈哈',
    }
  }

  click = (event, id, id1) => {
    console.log(event)
    console.log(id)
    console.log(id1)
  }

  render() {
    const { title } = this.state

    return (
      <div>
        <div onClick={this.click.bind(this, 10, 20)}>类组件点击事件</div>
      </div>
    )
  }
}

事件机制

React 基于浏览器的事件机制自身实现了一套事件机制,包括事件注册、事件的合成、事件冒泡、事件派发等

在 React 中这套事件机制被称之为合成事件

合成事件(SyntheticEvent)

合成事件是 React 模拟原生 DOM 事件所有能力的一个事件对象,即浏览器原生事件的跨浏览器包装器

根据 M3C 规范来定义合成事件,兼容所有浏览器,拥有与浏览器原生事件相同的接口,例如:

const button = <button onClick={handleClick}></button>

如果想要获得原生 DOM 事件,可以通过 e.nativeEvent 属性获取

const handleClick = (e) => console.log(e.nativeEvent)
const button = <button onClick={handleClick}>按钮</button>

从上面可以看到 React 事件和原生事件也非常的相似,但也有一定的区别:

事件名称命名方式不同

事件处理函数书写不同

虽然 onclick 看似绑定到 DOM 元素上,但实际并不会把事件代理函数直接绑定到真实的节点上,而是把所有的事件绑定到结构的最外层,使用一个统一的事件去监听

这个事件监听器上维持了一个映射来保存所有组件内部的事件监听和处理函数。当组件挂载或卸载时, 只是在这个统一的事件监听器上插入或删除一些对象

当事件发生时,首先被这个统一的事件监听器处理,然后在映射里找到真正的事件处理函数并调用。这样做简化了事件处理和回收机制,效率也有很大提升

执行顺序

关于 React 合成事件与原生事件执行顺序,可以看看下面一个例子:

结论:

  • React 所有事件都挂载在 document 对象上
  • 当真实 DOM 元素触发事件,会冒泡到 document 对象后,再处理 React 事件
  • 所以会先执行原生事件,然后处理 React 事件
  • 最后真正执行 document 上挂载的事件

对应过程如图所示:

所以想要阻止不同时间段的冒泡行为,对应使用不同的方法,对应如下:

  • 阻止合成事件间的冒泡,用e.stopPropagation()
  • 阻止合成事件与最外层 document 上的事件间的冒泡,用e.nativeEvent.stoplmmediatePropagation()
  • 阻止合成事件与除最外层document上的原生事件上的冒泡,通过判断e.target来避免

React 事件机制总结如下

事件绑定的方式

在 react应用中,事件名都是用小驼峰格式进行书写,例如 onclick 要改写成 onCLick

如何绑定

为了解决上面正确输出 this 的问题,常见的绑定方式有如下:

render方法中使用bind

如果使用一个类组件,在其中给某个组件/元素一个:onClick 属性,它现在并会自定绑定其 this 到当前组件,解决这个问题的方法是在事件函数后使用 .bind(this)将 this 绑定到当前组件中

这种方式在组件每次 render 渲染的时候,都会重新进行 bind 的操作,影响性能

render方法中使用箭头函数

通过 ES6 的上下文来将 this 的指向绑定给当前组件,同样再每一次 render 的时候都会生成新的方法,影响性能

constructor中bind

在 constructor 中预先 bind 当前组件,可以避免在 render 操作中重复绑定

定义阶段使用箭头函数绑定

跟上述方式三一样,能够避免在 render 操作中重复绑定,实现也非常的简单,如下:

上述四种方法的方式,区别主要如下:

  • 编写方面:方式一、方式二写法简单,方式三的编写过于冗杂
  • 性能方面:方式一和方式二在每次组件render的时候都会生成新的方法实例,性能问题欠缺。若该函数作为属性值传给子组件的时候,都会导致额外的渲染。而方式三、方式四只会生成一个方法实例
  • 综合上述,方式四是最优的事件绑定方式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2133405.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

论文复现--基于LeNet网络结构的数字识别

前言 一直就听说学习深度学习无非就是看论文&#xff0c;然后复现&#xff0c;不断循环&#xff0c;这段时间也看了好几篇论文(虽然都是简单的)&#xff0c;但是对于我一个人自学&#xff0c;复现成功&#xff0c;我感觉还是挺开心的 本人初学看论文的思路&#xff1a;聚焦网络…

2-93 基于matlab的无人机FMCW(频率调制连续波)毫米波高度计雷达仿真

基于matlab的无人机FMCW&#xff08;频率调制连续波&#xff09;毫米波高度计雷达仿真&#xff0c;不考虑环境杂波和收发信号隔离泄漏。通过考虑雷达天线、波束形成、信号传播、回波接收等环节影响。建立FMCW毫米波雷达系统的数学模型&#xff0c;评估无人机在不同高度下的高度…

区块链先驱孙宇晨:引领价值传播,激发行业创新活力

​孙宇晨&#xff0c;这位被誉为“区块链布道师”的年轻企业家&#xff0c;以其独特的愿景和行动力在区块链行业中脱颖而出。作为波场TRON的创始人&#xff0c;他不仅是区块链技术的倡导者&#xff0c;更是一位不懈推动行业发展的领导者。他通过自身的努力和影响力&#xff0c;…

vxe-table之复选框 设置父子节点不互相关联

通过 checkStrictly 设置父子节点不互相关联&#xff0c;默认不显示头部复选框&#xff0c;可以通过 checkbox-config{showHeader} 设置 <template><div><vxe-table:column-config"{resizable: true}":data"tableData":tree-config"{…

Python 数学建模——Fitter 拟合数据样本的分布

文章目录 介绍代码实例 介绍 数学建模中很多时候&#xff0c;我们有某个随机变量 X X X 的若干样本 X 1 , X 2 , ⋯ , X n X_1,X_2,\cdots,X_n X1​,X2​,⋯,Xn​&#xff0c;想要还原随机变量 X X X 的概率密度函数 f ( x ) f(x) f(x)。诚然&#xff0c;高斯核密度估计可以…

使用WinRAR把bat文件打包成exe

WinRAR下载 官网地址&#xff1a;https://www.winrar.com.cn/#:~:text64%E4%BD%8D%20WinRAR 百度网盘: https://pan.baidu.com/s/16m8Hph3sHqR2hmrJpY0yKQ 提取码: 3340 选择bat文件&#xff0c;点击添加压缩 名称改成exe&#xff0c;压缩选项那块自动勾选 切换到高级&am…

王者荣耀改重复名(java源码)

王者荣耀改重复名 项目简介 “王者荣耀改重复名”是一个基于 Spring Boot 的应用程序&#xff0c;用于生成王者荣耀游戏中的唯一名称。通过简单的接口和前端页面&#xff0c;用户可以输入旧名称并获得一个新的、不重复的名称。 功能特点 生成新名称&#xff1a;提供一个接口…

PAM后门从入门到应急响应

目录 1. PAM与PAM后门介绍 PAM介绍 PAM后门 2.植入PAM后门 2.1 关闭 selinux 和临时关闭 setenforce 2.2 查看 pam 版本号 2.3 下载对应版本的 pam 源码包 2.4 安装gcc编译器和flex库 2.5 留 PAM 后门和保存 ssh登录的账户密码 2.6 编译 2.9 登录测试 3. 应急响应…

Synchronized的底层实现

Synchronized用法 Synchronized 是 Java 中的一个重要关键字&#xff0c;主要是用来加锁的。在使用Synchronized的时候需要指定一个对象&#xff0c;所以synchornized也被称为对象锁。 synchronized 的使用方法比较简单&#xff0c;主要可以用来修饰方法和代码块。根据其锁定…

PLC远程调试-无需硬件设备-V3.2.0发布

文章目录 前言一、无缝连接&#xff0c;突破距离限制二、高效调试&#xff0c;提升工作效率三、安全可靠&#xff0c;保护数据安全四、用户友好&#xff0c;简化操作流程五、软件地址六、远程调试软件 七、基本操作1、订阅主题2、连接3、串口调试4、网口调试 八、软件地址结束语…

【数据结构精讲】01绪论(基本概念介绍和时间复杂度计算)

绪论 在绪论这部分会介绍常用的一些基本概念&#xff0c;让同学们对这门课的整体有所了解&#xff01; 数据结构以及相关概念 一、几个重要概念 1、数据&#xff1a;凡是能输入到计算机并能被计算机程序处理的符号的总称 2、数据元素&#xff1a;数据的基本单位&#xff0…

C++实现单向链表操作(实验3--作业)

一、单向链表介绍 单向链表是一种常见的数据结构&#xff0c;它由一系列节点组成&#xff0c;每个节点包含一个数据域和一个指向下一个节点的指针域。 结构特点&#xff1a; 单向链表的节点通过指针链接在一起&#xff0c;形成一个线性的数据结构。链表的头节点通常是一个特殊…

【Google Chrome Windows 64 version及 WebDriver 版本】

最近升级到最新版本Chrome后发现页面居然显示错乱实在无语, 打算退回原来的版本, 又发现官方只提供最新的版本下载, 为了解决这个问题所有收集了Chrome历史版本的下载地址分享给大家. Google Chrome Windows version 64 位 VersionSize下载地址Date104.0.5112.10282.76 MBhtt…

leetcode 2398.预算内的最多机器人数目

2398.预算内的最多机器人数目 题意&#xff1a; 解析&#xff1a; 需要注意的是&#xff0c;题目询问中连续是子数组的意思&#xff0c;即求满足条件的最长子数组的长度。 因为是连续的&#xff0c;所以可以用双指针扫描整个数组。每次将右指针 r r r 向右移动一个位置&…

Vue的缓存组件 | 详解KeepAlive

引言 在Vue开发中&#xff0c;我们经常需要处理大量的组件渲染和销毁操作&#xff0c;这可能会影响应用的性能和用户体验。而Vue的KeepAlive组件提供了一种简便的方式来优化组件的渲染和销毁流程&#xff0c;通过缓存已经渲染的组件来提升应用的性能。 本文将详细介绍Vue的Ke…

2024年职场人士PDF转换的新宠儿

PDF文件已经成为日常收发文件的首选项了&#xff0c;它能保证文件页面内容、版式的统一性&#xff0c;但是不太好编辑&#xff0c;所以我们还需要一些小工具来帮我们解决这个问题。今天我们来探讨一下有什么可以从pdf转换器免费版官网下载的工具。 1.Foxit PDF转换大师 链接一…

BuripSuiteProfessional 抓取HTTPS配置

1.电脑拿开代理 谷歌为例 点击三点-设置 -输入代理--点击代理设置 打开手动代理---IP ,端口如图-点击保存 2.下载CA证书 打开代理后,谷歌浏览器打开,输入/burp--如下图-点击CA证书下载证书 选择下载目录--桌面 3.安装CA证书 谷歌浏览器中点开设置-输入证书-点击安全 点击…

Apollo(阿波罗)架构由浅入深剖析

1.最简架构 如果不考虑分布式微服务架构中的服务发现问题,Apollo 的最简架构如下图所示: 注意事项: ConfigService 是一个独立的微服务,服务于 Client 进行配置获取。 Client 和 ConfigService 保持长连接,通过一种拖拉结合 (push & pull) 的模式,实现配置实时更新…

【鸿蒙】HarmonyOS NEXT星河入门到实战8-自定义组件-组件通信

目录 1、模块化语法 1.1 模块化基本认知 1.2 默认导出和导入 1.2.1 在ets下新建tools目录 1.2.2 在tools下新建moduls.ets文件 1.2.3 index.ets 1.3 按需导出和导入 1.4 全部导入 2、自定义组件 -基础 2.1 自定义组件 - 基本使用 2.2 自定义组件 -通用样式 2.2.1 et…

Wophp靶场寻找漏洞练习

1.命令执行漏洞 打开网站划到最下&#xff0c;此处的输入框存在任意命令执行漏洞 输入命令whoami 2.SQL注入 搜索框存在SQL注入&#xff0c;类型为整数型 最终结果可以找到管理员账户和密码 3.任意文件上传漏洞 在进入管理员后台后&#xff0c;上传木马文件 访问该文件&…