React 18 响应事件
参考文章
响应事件
使用 React 可以在 JSX 中添加 事件处理函数。其中事件处理函数为自定义函数,它将在响应交互(如点击、悬停、表单输入框获得焦点等)时触发。
添加事件处理函数
如需添加一个事件处理函数,需要先定义一个函数,然后 将其作为 prop 传入 合适的 JSX 标签。例如,这里有一个没绑定任何事件的按钮:
export default function Button() {return (<button>未绑定任何事件</button>);
}
按照如下三个步骤,即可让它在用户点击时显示消息:
- 在
Button组件 内部 声明一个名为handleClick的函数。 - 实现函数内部的逻辑(使用
alert来显示消息)。 - 添加
onClick={handleClick}到<button>JSX 中。
export default function Button() {function handleClick() {alert('你点击了我!');}return (<button onClick={handleClick}>点我</button>);
}
可以定义 handleClick 函数,然后 将其作为 prop 传入 <button>。其中 handleClick 是一个 事件处理函数 。事件处理函数有如下特点:
- 通常在组件 内部 定义。
- 名称以
handle开头,后跟事件名称。
按照惯例,通常将事件处理程序命名为
handle,后接事件名。会经常看到onClick={handleClick},onMouseEnter={handleMouseEnter}等。
或者,也可以在 JSX 中定义一个内联的事件处理函数:
<button onClick={function handleClick() {alert('你点击了我!');
}}>
或者,直接使用更为简洁的箭头函数:
<button onClick={() => {alert('你点击了我!');
}}>
以上所有方式都是等效的。当函数体较短时,内联事件处理函数会很方便。
传递函数
传递给事件处理函数的函数应直接传递,而非调用。例如:
| 传递一个函数(正确) | 调用一个函数(错误) |
|---|---|
<button onClick={handleClick}> | <button onClick={handleClick()}> |
区别很微妙。在第一个示例中,handleClick 函数作为 onClick 事件处理函数传递。这会让 React 记住它,并且只在用户点击按钮时调用函数。
在第二个示例中,handleClick() 中最后的 () 会在 渲染 过程中 立即 触发函数,即使没有任何点击。这是因为在 JSX { 和 } 之间的 JavaScript 会立即执行。
当编写内联代码时,同样的陷阱可能会以不同的方式出现:
| 传递一个函数(正确) | 调用一个函数(错误) |
|---|---|
<button onClick={() => alert('...')}> | <button onClick={alert('...')}> |
如果按如下方式传递内联代码,并不会在点击时触发,而是会在每次组件渲染时触发:
// 这个 alert 在组件渲染时触发,而不是点击时触发!<button onClick={alert('你点击了我!')}>
如果想要定义内联事件处理函数,请将其包装在匿名函数中,如下所示:
<button onClick={() => alert('你点击了我!')}>
这里创建了一个稍后调用的函数,而不会在每次渲染时执行其内部代码。
在这两种情况下,都应该传递一个函数:
<button onClick={handleClick}>传递了handleClick函数。<button onClick={() => alert('...')}>传递了() => alert('...')函数。
在事件处理函数中读取 props
由于事件处理函数声明于组件内部,因此它们可以直接访问组件的 props。示例中的按钮,当点击时会弹出带有 message prop 的 alert:
function AlertButton({ message, children }) {return (<button onClick={() => alert(message)}>{children}</button>);
}export default function Toolbar() {return (<div><AlertButton message="正在播放!">播放电影</AlertButton><AlertButton message="正在上传!">上传图片</AlertButton></div>);
}
此处有两个按钮,会展示不同的消息。可以尝试更改传递给它们的消息。
将事件处理函数作为 props 传递
通常,会在父组件中定义子组件的事件处理函数。比如:置于不同位置的 Button 组件,可能最终执行的功能也不同 —— 也许是播放电影,也许是上传图片。
为此,将组件从父组件接收的 prop 作为事件处理函数传递,如下所示:
function Button({ onClick, children }) {return (<button onClick={onClick}>{children}</button>);
}function PlayButton({ movieName }) {function handlePlayClick() {alert(`正在播放 ${movieName}!`);}return (<Button onClick={handlePlayClick}>播放 "{movieName}"</Button>);
}function UploadButton() {return (<Button onClick={() => alert('正在上传!')}>上传图片</Button>);
}export default function Toolbar() {return (<div><PlayButton movieName="魔女宅急便" /><UploadButton /></div>);
}
示例中,Toolbar 组件渲染了一个 PlayButton 组件和 UploadButton 组件:
PlayButton将handlePlayClick作为onClickprop 传入Button组件内部。UploadButton将() => alert('正在上传!')作为onClickprop 传入Button组件内部。
最后,Button 组件接收一个名为 onClick 的 prop。它直接将这个 prop 以 onClick={onClick} 方式传递给浏览器内置的 <button>。当点击按钮时,React 会调用传入的函数。
如果遵循某个 设计系统 时,按钮之类的组件通常会包含样式,但不会指定行为。而 PlayButton 和 UploadButton 之类的组件则会向下传递事件处理函数。
命名事件处理函数 prop
内置组件(<button> 和 <div>)仅支持 浏览器事件名称,例如 onClick。但是,当构建自己的组件时,可以按个人喜好命名事件处理函数的 prop。
按照惯例,事件处理函数 props 应该以
on开头,后跟一个大写字母。
例如,Button 组件的 onClick prop 本来也可以被命名为 onSmash:
function Button({ onSmash, children }) {return (<button onClick={onSmash}>{children}</button>);
}export default function App() {return (<div><Button onSmash={() => alert('正在播放!')}>播放电影</Button><Button onSmash={() => alert('正在上传!')}>上传图片</Button></div>);
}
上述示例中,<button onClick={onSmash}> 代表浏览器内置的 <button>(小写)仍然需要使用 onClick prop,而自定义的 Button 组件接收到的 prop 名称可由你决定!
当组件支持多种交互时,可以根据不同的应用程序命名事件处理函数 prop。例如,一个 Toolbar 组件接收 onPlayMovie 和 onUploadImage 两个事件处理函数:
export default function App() {return (<ToolbaronPlayMovie={() => alert('正在播放!')}onUploadImage={() => alert('正在上传!')}/>);
}function Toolbar({ onPlayMovie, onUploadImage }) {return (<div><Button onClick={onPlayMovie}>播放电影</Button><Button onClick={onUploadImage}>上传图片</Button></div>);
}function Button({ onClick, children }) {return (<button onClick={onClick}>{children}</button>);
}
注意:App 组件并不需要知道 Toolbar 将会对 onPlayMovie 和 onUploadImage 做 什么 。上述示例是 Toolbar 的实现细节。其中,Toolbar 将它们作为 onClick 处理函数传递给了 Button 组件,其实还可以通过键盘快捷键来触发它们。根据应用程序特定的交互方式(如 onPlayMovie)来命名 prop ,可以让你灵活地更改以后使用它们的方式。
注意:确保为事件处理程序使用适当的HTML标记。例如,要处理点击,要使用<button onClick={handleClick}> 而不是 <div onClick={handleClick}> 。使用真正的浏览器<button>可以实现内置的浏览器行为,如键盘导航。如果不喜欢按钮的默认浏览器样式,并想让它看起来更像一个链接或不同的UI元素,可以用CSS来实现。
事件传播
事件处理函数还将捕获任何来自子组件的事件。通常,我们会说事件会沿着树向上“冒泡”或“传播”:它从事件发生的地方开始,然后沿着树向上传播。
下面这个 <div> 包含两个按钮。<div> 和每个按钮都有自己的 onClick 处理函数。你认为点击按钮时会触发哪些处理函数?
export default function Toolbar() {return (<div className="Toolbar" onClick={() => {alert('你点击了 toolbar !');}}><button onClick={() => alert('正在播放!')}>播放电影</button><button onClick={() => alert('正在上传!')}>上传图片</button></div>);
}
如果点击任一按钮,它自身的 onClick 将首先执行,然后父级 <div> 的 onClick 会接着执行。因此会出现两条消息。如果点击 toolbar 本身,将只有父级 <div> 的 onClick 会执行。
注意:在 React 中所有事件都会传播,除了 onScroll,它仅适用于你附加到的 JSX 标签。
阻止传播
事件处理函数接收一个 事件对象 作为唯一的参数。按照惯例,它通常被称为 e ,代表 “event”(事件)。可以使用此对象来读取有关事件的信息。
这个事件对象还允许阻止传播。如果想阻止一个事件到达父组件,需要像下面 Button 组件那样调用 e.stopPropagation() :
function Button({ onClick, children }) {return (<button onClick={e => {e.stopPropagation();onClick();}}>{children}</button>);
}export default function Toolbar() {return (<div className="Toolbar" onClick={() => {alert('你点击了 toolbar !');}}><Button onClick={() => alert('正在播放!')}>播放电影</Button><Button onClick={() => alert('正在上传!')}>上传图片</Button></div>);
}
当点击按钮时:
- React 调用了传递给
<button>的onClick处理函数。 - 定义在
Button中的处理函数执行了如下操作:- 调用
e.stopPropagation(),阻止事件进一步冒泡。 - 调用
onClick函数,它是从Toolbar组件传递过来的 prop。
- 调用
- 在
Toolbar组件中定义的函数,显示按钮对应的 alert。 - 由于传播被阻止,父级
<div>的onClick处理函数不会执行。
由于调用了 e.stopPropagation(),点击按钮现在将只显示一个 alert(来自 <button>),而并非两个(分别来自 <button> 和父级 toolbar <div>)。点击按钮与点击周围的 toolbar 不同,因此阻止传播对这个 UI 是有意义的。
传递处理函数作为事件传播的替代方案
注意,此处的点击事件处理函数先执行了一行代码,然后调用了父组件传递的 onClick prop:
function Button({ onClick, children }) {return (<button onClick={e => {e.stopPropagation();onClick();}}>{children}</button>);
}
也可以在调用父元素 onClick 函数之前,向这个处理函数添加更多代码。此模式是事件传播的另一种 替代方案 。它让子组件处理事件,同时也让父组件指定一些额外的行为。与事件传播不同,它并非自动。但使用这种模式的好处是可以清楚地追踪因某个事件的触发而执行的整条代码链。
如果依赖于事件传播,而且很难追踪哪些处理程序在执行,及其执行的原因,可以尝试这种方法。
阻止默认行为
某些浏览器事件具有与事件相关联的默认行为。例如,点击 <form> 表单内部的按钮会触发表单提交事件,默认情况下将重新加载整个页面:
export default function Signup() {return (<form onSubmit={() => alert('提交表单!')}><input /><button>发送</button></form>);
}
可以调用事件对象中的 e.preventDefault() 来阻止这种情况发生:
export default function Signup() {return (<form onSubmit={e => {e.preventDefault();alert('提交表单!');}}><input /><button>发送</button></form>);
}
不要混淆 e.stopPropagation() 和 e.preventDefault()。它们都很有用,但二者并不相关:
e.stopPropagation()阻止触发绑定在外层标签上的事件处理函数。e.preventDefault()阻止少数事件的默认浏览器行为。
事件处理函数可以包含副作用
当然可以!事件处理函数是执行副作用的最佳位置。
与渲染函数不同,事件处理函数不需要是 纯函数,因此它是用来 更改 某些值的绝佳位置。例如,更改输入框的值以响应键入,或者更改列表以响应按钮的触发。但是,为了更改某些信息,首先需要某种方式存储它。在 React 中,这是通过 state(组件的记忆) 来完成的。
摘要
- 可以通过将函数作为 prop 传递给元素如
<button>来处理事件。 - 必须传递事件处理函数,而非函数调用!
onClick={handleClick},不是onClick={handleClick()}。 - 可以单独或者内联定义事件处理函数。
- 事件处理函数在组件内部定义,所以它们可以访问 props。
- 可以在父组件中定义一个事件处理函数,并将其作为 prop 传递给子组件。
- 可以根据特定于应用程序的名称定义事件处理函数的 prop。
- 事件会向上传播。通过事件的第一个参数调用
e.stopPropagation()来防止这种情况。 - 事件可能具有不需要的浏览器默认行为。调用
e.preventDefault()来阻止这种情况。 - 从子组件显式调用事件处理函数 prop 是事件传播的另一种优秀替代方案。
相关文章:
React 18 响应事件
参考文章 响应事件 使用 React 可以在 JSX 中添加 事件处理函数。其中事件处理函数为自定义函数,它将在响应交互(如点击、悬停、表单输入框获得焦点等)时触发。 添加事件处理函数 如需添加一个事件处理函数,需要先定义一个函数…...
面试总结-c++
1该吹牛逼吹牛逼。在自己能说出个所以然的情况下,该吹就吹,不吹没工作,吹了有希望。 比如 c组长,确有其事,但是挺唬人。说自己在北京定居也是侧面吹牛逼,证明自己的能力。还有媳妇在研究所。 2.对自己做过…...
Spring(九) - 解惑 spring 嵌套事务.2
1. 事务传播特性 在所有使用 spring 的应用中, 声明式事务管理可能是使用率最高的功能了, 但是, 从我观察到的情况看,绝大多数人并不能深刻理解事务声明中不同事务传播属性配置的的含义, 让我们来看一下 TransactionDefinition 接口中的定义 Java代码 /** * Support a cu…...
Android Studio API 33 获取当前连接的WIFI名称
常规流程失败流程 常规流程 以下内容在 API 33 成功实现,低版本API还请自行尝试(仅推荐 API 29 - 33 用户食用) 先(至少)添加以下权限到你的 AndroidManifest.xml 文件 <uses-permission android:name"andr…...
ICCV 2023 | 半监督三维目标检测新SOTA:密集匹配和量化补偿
论文链接:https://arxiv.org/abs/2304.13031 开源代码仓库地址:https://github.com/AIR-DISCOVER/DQS3D 方法效果对比图:有效在半监督情况下处理临近小物体 01. 简介 本文旨在解决三维室内场景中高昂的标注成本问题,特别关注半监…...
python+django+mysql项目实践三(用户管理)
python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 用户列表展示 urls view models html <!DOCTYPE html> <html...
Java多线程 | 操作线程的方法详解
文章目录 一、线程的启动1.1 start()方法 二、线程的休眠与中断2.1 Thread.sleep()方法2.2 interrupt()方法 三、线程的等待与唤醒3.1 wait()方法3.2 Object类的notify()和notifyAll()方法3.3 await()和signal()方法3.4 使用join()方法等待线程执行完成 四、线程的状态控制与管…...
【ConcurrentHashMap1.7源码】十分钟带你深入ConcurrentHashMap并发解析
ConcurrentHashMap1.7源码 四个核心要点 初始化PUT扩容GET Unsafe 初始化 五个构造方法 /*** Creates a new, empty map with the default initial table size (16).*/public ConcurrentHashMap() {}/*** Creates a new, empty map with an initial table size* accommodati…...
程序框架-事件中心模块-观察者模式
一、观察者模式 1.1 观察者模式定义 意图: 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变是,所有依赖于它的对象都能得到通知并自动更新。 适用性: 当一个对象状态的改变需要改变其他对象, 或实际对…...
通过AOP的ProceedingJoinPoint获取方法信息
文章目录 ProceedingJoinPoint用法 ProceedingJoinPoint用法 获得切点对应的方法(Method) 本处Method指的是java.lang.reflect.Method 若切入点表达式是方法,则获得的是切入点方法的信息。若切入点表达式是注解,则获得的是使用了…...
【JavaSE】初步认识类和对象
【本节目标】 1. 掌握类的定义方式以及对象的实例化 2. 掌握类中的成员变量和成员方法的使用 3. 掌握对象的整个初始化过程 目录 1. 面向对象的初步认知 2. 类定义和使用 3. 类的实例化 4. this引用 1. 面向对象的初步认知 1.1 什么是面向对象 Java是一门纯面向对象的语…...
python中的matplotlib画饼图(数据分析与可视化)
直接开始 1、先安装pandas和matplotlib pip install pandas pip install matplotlib2、然后在py文件中导入 import pandas as pd import matplotlib.pyplot as plt3、然后直接写代码 import pandas as pd import matplotlib.pyplot as pltpd.set_option("max_columns&…...
用Rust实现23种设计模式之 职责链模式
关注我,学习Rust不迷路!! 优点 解耦:职责链模式将请求发送者和接收者解耦,使得多个对象都有机会处理请求,而不是将请求的发送者和接收者紧密耦合在一起。灵活性:可以动态地改变或扩展处理请求…...
进销存管理中的技术创新和数字化转型
在进销存管理中,技术创新和数字化转型可以通过以下具体的应用案例来实现: 自动化仓储系统:利用自动化技术和机器人系统来管理仓库操作,包括货物的装卸、分拣和存储。这可以提高仓库的运作效率,减少人力成本࿰…...
与“云”共舞,联想凌拓的新科技与新突破
伴随着数字经济的高速发展,IT信息技术在数字中国建设中起到的驱动和支撑作用也愈发凸显。特别是2023年人工智能和ChatGPT在全球的持续火爆,更是为整个IT产业注入了澎湃动力。那么面对日新月异的IT信息技术,再结合疫情之后截然不同的经济环境和…...
【超细节】Vue3组件事件怎么声明,defineEmits与emit
目录 前言 一、基本语法 1. 子组件触发 2. 父组件监听 二、 事件参数 1. 传值 2. 接收值 三、 事件校验 四、注意事项 前言 组件事件是 Vue 组件之间进行通信的一种方式。它允许一个组件触发一个自定义事件,并且其他组件可以监听并响应这个事件。 一、基本…...
java Selenium 实现简单的网页操作
官方文档:入门指南 | Selenium Selenium是一个用于Web应用测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。 所以使用这个前端测试话工具,可以自动化做很多事情,比如自动化抓取网页内容,俗称网…...
(数据库系统概论|王珊)第一章绪论-第一节:数据库系统概论
目录 一:四大基本概念 (1)数据(Data) (2)数据库(DataBase,DB) (3)数据库管理系统(DataBase Management System,DBMS) (4)数据库系统(Database System,DBS…...
深入理解TCP三次握手:连接可靠性与安全风险
目录 导言TCP简介和工作原理的回顾TCP三次握手的目的和步骤TCP三次握手过程中可能出现的问题和安全风险为什么TCP三次握手是必要的?是否可以增加或减少三次握手的次数?TCP四次挥手与三次握手的异同点 导言 在网络通信中,TCP(Tra…...
基于人工智能的智能矿山解决方案
什么是智能矿山? 智能矿山是一种运用先进技术和智能化系统来管理和监控矿山运营的概念。它利用传感器、无线通信、数据分析和人工智能等技术,实现对矿山内部各个环节的实时监测、自动化控制和智能决策,从而提高矿山的效率、安全性和可持续性。…...
设计模式和设计原则回顾
设计模式和设计原则回顾 23种设计模式是设计原则的完美体现,设计原则设计原则是设计模式的理论基石, 设计模式 在经典的设计模式分类中(如《设计模式:可复用面向对象软件的基础》一书中),总共有23种设计模式,分为三大类: 一、创建型模式(5种) 1. 单例模式(Sing…...
盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
关于 WASM:1. WASM 基础原理
一、WASM 简介 1.1 WebAssembly 是什么? WebAssembly(WASM) 是一种能在现代浏览器中高效运行的二进制指令格式,它不是传统的编程语言,而是一种 低级字节码格式,可由高级语言(如 C、C、Rust&am…...
【Oracle】分区表
个人主页:Guiat 归属专栏:Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...
C++.OpenGL (14/64)多光源(Multiple Lights)
多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
Proxmox Mail Gateway安装指南:从零开始配置高效邮件过滤系统
💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「storms…...
认识CMake并使用CMake构建自己的第一个项目
1.CMake的作用和优势 跨平台支持:CMake支持多种操作系统和编译器,使用同一份构建配置可以在不同的环境中使用 简化配置:通过CMakeLists.txt文件,用户可以定义项目结构、依赖项、编译选项等,无需手动编写复杂的构建脚本…...
