在大多的Web页面或Web应用程序中,构建UI仅仅是其中小小的一部分,其中有些U是纯静态的,但有些UI是会随着用户的操作带来相应变化的。比如,用户通过鼠标点击,键盘按下,视窗大小调整或一系列其他的手势和交互来触发。而这些我们往往称之为事件。在Web中处理事件都是交给JavaScript来完成,同样的,在React中也有相应的事件。在这一节开始,我们就来学习React中的事件。
你可能在DOM的世界中对事件的使用有一定的了解。如果没有,也不用担心,我们在开启探讨React的事件之前会对JavaScript中的事件做一个初步的了解。
JavaScript中的事件
Web页面或Web应用程序总是会提供一些控件给用户操作的。简单地说,用户做了一个操作,会发生什么。或者说,应用程序用什么方法让它们对已知的事件作出反应。
在开始之前,我们必须了解事件是什么?这个很简单,在Web中创建的所有东西都可以通过以下的语句来建模:
当(...)发生,做(...)?
在Web中我们可以用无数种不同的方法来填补这句话中的空白之处。第一个()表示发生了什么?第二个()描述相应的操作会发生什么?比如下面这些例子:
当(页面加载)完成时,请(播放视频)
当(点击)发生时,请(提交表单)
当(鼠标释放)时,请(放大图片)
当(按下删除键)时,请将此(列表项删除)
当(触摸手势)发生时,请将(旧照片过滤掉)
当(文件下载)完时,请(更新进度条)
这种模型适用于我们所有的编码。不过,事件只不过是一个信号。它表示刚刚发生了什么事。而这个事件是可以鼠标点击、键盘按下、触摸手势等。回到我们所说的事件模型中,模型的前半部分是我们所说的事件,后面部分是事件反应:
用到我们生活中来的话,有点类似于我们发送了一个信号,等事物接到这个信号就会做出一个对应的响应。而且在JavaScript中,事件又是非常重要的。如果用专业术语来描述的话,其包含两个部分:
事件侦听
事件做出的反应
这两个步骤看起来非常简单,但是不要忘记这是在JavaScript中处理。如果我们稍微走错了一步,都会给我们的应用程序带来巨大的创伤。
事件侦听
在JavaScript中有三种方式可以为DOM元素注册事件处理函数(即给元素添加事件侦听)。
addEventListener
最常见的就是通过addEventListener来给目标元素添加事件侦听。比如:
myButton.addEventListener('click', function(){
alert('Hello, World!')
}, false)
HTMl属性
我们可以在HTML元素中添加事件:
Click
DOM元素属性
我们也可以直接给一个DOM元素属性添加相应的事件:
myButton.onclick = function(e){alert('Hello, World!')}
通常情况下,我们都习惯于使用addEventListener来给目标元素(DOM元素)添加事件:
targetElement.addEventListener(eventName, eventHandler, false)
简单介绍一下其组成部分:
targetElement:要侦听事件的元素或对象。通常是一个DOM元素,也可以是document、window或任何专门用于触发事件的对象
eventName:事件名称,在JavaScript中有关于事件的名称列表可以点击这里查阅
eventHandler:事件处理程序,就是程序要做的事情(比如用户点击了按钮,会发生什么事情?)
事件冒泡或捕获:这是最后一个参数,在JavaScript中指的是事件冒泡或捕获
放到一起之后,他可能像下面这样:
btnEle.addEventListener('click',showMessage('大漠'), false)
const showMessage = (name) => {
alert(`Hello, ${name}~`)
}
事件捕获或冒泡
在JavaScript事件中还有一个很重要的概念,也比前面有关于事件的基础知识更为复杂,那就是事件捕获和事件冒泡。为了更好的帮助我们理解事件中有关于这两方面的概念,使用一个简单的示例来向大家阐述。
one
two
three
如果我们用DOM树来描述上面的HTML结构的话,大致像下图这样:
假设用户点击了buttonOne按钮,即触发了一个click事件。很多同学都会认为click事件是从目标元素buttonOne开始触发,事实上并非如此,click事件从文档的根开始(即window)。如果用图来描述的话,如下;
click事件从文档根(window)开始,然后按照DOM树的路径一级一级往下寻找,直到触发click事件的butttOne元素(也称为事件目标)停止:
正如上图所示,事件所经过的路径是直的,它会通知该路径上的每个元素。如果该路径上有与当前事件匹配的元素,那么就会调用对应的事件处理程序。一旦事件达到目标,它就不会停止。不同的是,事件会往上移。事件路径上的每个元素都会得到其存在的通知。出现的任何事件处理程序也将被调用。
需要注意的是,事件在哪个位置启动并不重要,因为事件总是从文档的根开始,向下直接到达目标,然后返回根。在这样的一个过程中,从根开始向下寻找事件目标的过程被称为事件捕获阶段:
反过来,事件从目标元素向文档根元素的过程被称为事件冒泡阶段:
也就是说,当一个事件被触发时,会得到两次通知。每次监听事件时,我们都会选择要监听哪个阶段的事件。这是一个细节,我们可以给addEventListener指定true或false来进行设置:
true:表示在捕获阶段侦听事件
false:表示在冒泡阶段侦听事件
有的时候我们需要结束事件的生命周期。只需要在事件对象上使用stopPropagation方法即可:
const handleClick = (e) => {
e.stopPropagation()
}
上面这些是JavaScript中事件的基础知识,如果你想了解更多有关于JavaScript事件相关的知识,可以阅读下面这些教程:
React中的事件
基于我们现有的JavaScript经验,你可能已经非常习惯使用事件。但是,在React中处理事件的方式和JavaScript有所不同。React没有直接针对DOM事件,而是将它们包装在自己的事件包装器中。在接下来的小节中,我将和大家一起探讨和学习React中事件相关的知识点。
创建事件
为了更好的和大家聊React中的事件,我们从一个简单的示例开始。该示例创建一个包含input和button的表单控件。input可以输出你想要的文本内容,当input输入一个值时,将触发一个事件(一般是onChange)事件。另外,用户点击按钮时,也会触发一个事件(一般是onClick)事件,该事件会调用一个函数,该函数会将文本框的内容(文本)反转。
该示例大致是这样工作的:
一个input,可以让用户输入想要的内容
当用户在input中输入值时,将会触发onChange事件,它会调用一个handleChange()函数,该函数用于设置input的新状态
当用户点击“点击我”按钮时(button),会触发另一个事件,该事件会调用handleReverse()函数,将输入框的文本反转
该示例的代码大致如下:
const App = () => {
const [inputVal, setInputVal] = React.useState('')
const [reversedText, setReversedText] = React.useState('')
const handleChange = e => setInputVal(e.currentTarget.value)
const handleClick = e => {
e.preventDefault()
setReversedText(inputVal.split("").reverse().join(""))
}
return (
点击我
{reversedText}
)
}
上面示例中是React创建事件方式之一。如果你是使用类来创建的组件,那么组件中的创建事件方法可以像下面这样:
class MyComponent extends React.Component {
handleClick = () => {
// ...
}
render() {
return Click Me
}
}
如果使用React Hooks来创建组件的话,除了上例中useState()方法外,我们还可以使用useRef()方法:
// React Hooks中使用useState()创建事件
class MyComponent = () => {
const handleClick = e => useState()
return Click Me
}
// React Hooks中使用useRef()创建事件
class MyComponent = () => {
const handleChange = e => useRef()
return Clic