事件处理
React 中事件命名采用驼峰式书写,并且需要传入一个函数作为事件处理函数。在函数组件中,一个事件的处理函数常常作为组件内的函数存在,在实际定义时可以使用关键字function也可以直接定义一个匿名剪头函数。例如以下是一个最简单的响应<button>元素点击事件的例子。
function Button(props) {
  function handleClick() {
    alert("Clicked!");
  }
  return <button onClick={handleClick}>{props.children}</button>;
}
但是这种function中套function的写法看起来属实比较怪,所以在实际使用中,常常都是使用匿名剪头函数来定义,例如:
function Button(props) {
  const handleClick = () => {
    alert("Clicked!");
  };
  return <button onClick={handleClick}>{props.children}</button>;
}
甚至还可以直接在onClick的位置定义匿名函数。
function Button(props) {
  return <button onClick={() => alert("Clicked!")}>{props.children}</button>;
}
优化事件处理函数定义
与useMemo一样,在事件处理函数中如果访问了组件的 State 等内容,实际上也是一种开销。一种比较好的解决方法依旧是将事件处理函数缓存起来,等到它内部所使用的值发生变化的时候再重新渲染。
于是 React 中提供的另一个 Hook useCallback就派上用场了,useCallback可以在组件的多次渲染中缓存其所定义的函数。useCallback的使用方法与useMemo基本上一致,其接受的参数也是一个需要缓存的函数和一个dependencies依赖项列表。
在大部分情况下,React是不会丢弃函数的缓存的,也就是说在应用刚开始开发的时候是没有必要直接使用useCallback的,只有在某些计算过程变慢需要优化的时候才需要使用。如果应用中使用了虚拟列表等需要React丢弃函数缓存的功能,那么useCallback就更没有用武之地了。
事件冒泡
事件冒泡通常是指事件在触发以后,沿着组件树向组件树的根节点传播。在传统 HTML DOM 编程中,我们常常使用e.stopPropagation()来阻止事件继续冒泡。
但是如果需要在事件被阻止冒泡以后继续被捕获,那么就可以在组件树上使用事件带有Capture后缀的版本来对其进行捕获。例如:
function Compo() {
  return (
    <div
      onClickCapture={() => {
        /* 将先于子元素的onClick处理执行 */
      }}
    >
      <button onClick={() => e.stopPropagation()}>Click</button>
    </div>
  );
}
事件默认行为
在 HTML DOM 中,浏览器对一些事件是有默认处理行为的,例如<form>的onSubmit,会导致页面的刷新。要阻止这种默认行为,在 React 中的处理与 HTML DOM 中的处理一样,都是通过调用e.preventDefault()来实现的。
事件类型
React 对于常见的 HTML 元素标签都提供了封装,同时也提供了相应事件的封装。在定义事件处理函数时,可以使用 React 提供的类型来接收和处理元素产生的事件。在 React 中经过包装的事件被称为合成事件,即SyntheticEvent<E>类型对象。React 中的绝大多数事件类型都是从SyntheticEvent<E>类型派生而来。
例如可以这样捕获input产生的输入事件。
function Input() {
  const [value, setValue] = useState<string | null>(null);
  const handleInput = (e: InputEvent) => {
    setValue(e.data);
  };
  return <input value={value} onChange={handleInput} />;
}
React 将 HTML DOM 的原生事件包装进了SyntheticEvent的nativeEvent属性中,在必要的时候可以通过这个属性来访问浏览器的原生事件。除了nativeEvent以外,SyntheticEvent类型还提供了以下标准属性可供使用。
- bubbles:布尔值,用于表示事件是否会冒泡传播。
- cancelable:布尔值,用于表示事件是否可以被取消。
- currentTarget:DOM 节点,用来获取当前节点在 React DOM 树中的位置。
- defaultPrevented:布尔值,用来表示是否调用了- preventDefault()方法。
- eventPhase:数字值,用来表示当前事件所处的阶段。
- isTrusted:布尔值,用来返回事件是否由用户发起。
- target:DOM 节点,用来返回事件发生的节点。
- timestamp:数字值,用来返回事件发生的时间。
常用事件及其对应类型
以下事件监听属性适用于所有内置组件
| 事件监听属性 | 事件类型 | 触发时间 | 
|---|---|---|
| onAnimationEnd | AnimationEvent | 在 CSS 动画完成时 | 
| onAnimationIteration | AnimationEvent | 在 CSS 动画的一次迭代结束时 | 
| onAnimationStart | AnimationEvent | 在 CSS 动画开始时 | 
| onAuxClick | AnimationEvent | 当指针设备上非主要指针按钮被点击时 | 
| onBeforeInput | InputEvent | 在可编辑元素的值被修改之前 | 
| onBlur | FocusEvent | 在元素失去焦点时 | 
| onClick | MouseEvent | 当指针设备上的主按钮被点击时 | 
| onCompositionStart | CompositionEvent | 当输入法编辑器开始新的组合会话时 | 
| onCompositionEnd | CompositionEvent | 当输入法编辑器完成或者取消组合会话时 | 
| onCompositionUpdate | CompositionEvent | 在输入法编辑器收到一个新的字符时 | 
| onContextMenu | MouseEvent | 在用户尝试打开上下文菜单时 | 
| onCopy | ClipboardEvent | 在用户尝试将一些内容复制到剪贴板时 | 
| onCut | ClipboardEvent | 在用户尝试将一些内容剪切到剪贴板时 | 
| onDoubleClick | MouseEvent | 在用户双击时 | 
| onDrag | DragEvent | 在用户拖拽某些元素时 | 
| onDragEnd | DragEvent | 在用户停止拖拽某些元素时 | 
| onDragOver | DragEvent | 在被拖动的元素进入指定放置目标时 | 
| onDragStart | DragEvent | 在用户开始拖拽元素时 | 
| onDrop | DragEvent | 在元素被拖放到有效的目的区域时 | 
| onFocus | FocusEvent | 在元素获得焦点时 | 
| onGotPointerCapture | PointerEvent | 当元素以变成方式捕获指针时 | 
| onKeyDown | KeyboardEvent | 当按键被按下时 | 
| onKeyUp | KeyboardEvent | 当按键被释放时 | 
| onLostPointerCapture | PointerEvent | 当元素停止捕获指针时 | 
| onMouseDown | MouseEvent | 当指针被按下时 | 
| onMouseEnter | MouseEvent | 当指针在元素内移动时 | 
| onMouseLeave | MouseEvent | 当指针移动到元素外部时 | 
| onMouseMove | MouseEvent | 当指针改变坐标时 | 
| onMouseOut | MouseEvent | 当指针移动到元素外部或者移动到子元素时 | 
| onMouseUp | MouseEvent | 当指针释放时 | 
| onPointerDown | PointerEvent | 当浏览器取消指针交互时 | 
| onPointerEnter | PointerEvent | 当指针在元素内移动时 | 
| onPointerLeave | PointerEvent | 当指针移动到元素外部时 | 
| onPointerMove | PointerEvent | 当指针改变坐标时 | 
| onPointerOut | PointerEvent | 当指针移动到元素外部时 | 
| onPointerUp | PointerEvent | 当指针不再活动时 | 
| onPaste | ClipboardEvent | 当用户尝试从剪贴板粘贴内容时 | 
| onScroll | Event | 当元素被滚动时 | 
| onSelect | Event | 当可编辑元素内部的选择更改后 | 
| onTouchCancel | TouchEvent | 当浏览器取消触摸交互时 | 
| onTouchEnd | TouchEvent | 当一个或者多个触摸点被移除时 | 
| onTouchMove | TouchEvent | 当一个或者多个触摸点发生移动时 | 
| onTouchStart | TouchEvent | 当一个或者多个触摸点被放置时 | 
| onTransitionEnd | TransitionEvent | 当 CSS 过度完成时 | 
| onWheel | WheelEvent | 当用户旋转滚轮时 | 
以下事件监听属性适用于<form>元素
| 事件监听属性 | 事件类型 | 触发时间 | 
|---|---|---|
| onReset | Event | 当表单被重置时 | 
| onSubmit | Event | 当表单被提交时 | 
以下事件监听属性适用于<dialog>元素
| 事件监听属性 | 事件类型 | 触发时间 | 
|---|---|---|
| onCancel | Event | 当用户尝试关闭对话框时 | 
| onClose | Event | 当对话框已经被关闭时 | 
以下事件监听属性适用于<details>元素
| 事件监听属性 | 事件类型 | 触发时间 | 
|---|---|---|
| onToggle | Event | 当用户切换详细信息时 | 
以下事件监听属性适用于<img>、<iframe>、<object>、<embed>、<link>、SVG 中的<image>元素
| 事件监听属性 | 事件类型 | 触发时间 | 
|---|---|---|
| onLoad | Event | 当资源开始加载时 | 
| onError | Event | 当资源无法加载时 | 
以下事件监听属性适用于<audio>、<video>元素
| 事件监听属性 | 事件类型 | 触发时间 | 
|---|---|---|
| onAbort | Event | 当资源没有完全加载时 | 
| onCanPlay | Event | 当有足够的数据可以开始播放但没有缓冲到结束时 | 
| onCanPlayThrough | Event | 当有足够的数据且可以播放到结束时 | 
| onDurationChange | Event | 当媒体持续事件更新时 | 
| onEmptied | Event | 当媒体变为空时 | 
| onEncrypted | Event | 当浏览器遇到加密媒体时 | 
| onEnded | Event | 当因为没有剩余内容可以播放而停止时 | 
| onError | Event | 当资源无法播放时 | 
| onLoadedData | Event | 在当前播放帧已被加载时 | 
| onLoadedMetadata | Event | 当元数据加载完成时 | 
| onLoadStart | Event | 当浏览器开始加载资源时 | 
| onPause | Event | 当媒体暂停时 | 
| onPlay | Event | 当媒体不再暂停时 | 
| onPlaying | Event | 当媒体开始或者重新开始播放时 | 
| onProgress | Event | 在资源加载时定期触发 | 
| onRateChange | Event | 当播放帧率改变时 | 
| onResize | Event | 当视频大小发生改变时 | 
| onSeeked | Event | 当搜索操作完成时 | 
| onSeeking | Event | 当搜索操作开始时 | 
| onStalled | Event | 当浏览器等待数据时 | 
| onSuspend | Event | 当资源加载被暂停时 | 
| onTimeUpdate | Event | 当播放时间更新时 | 
| onVolumeChange | Event | 当音量发生变化时 | 
| onWaiting | Event | 当临时缺少数据而播放停止时 |