事件处理

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依赖项列表。

Tip

在大部分情况下,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 的原生事件包装进了SyntheticEventnativeEvent属性中,在必要的时候可以通过这个属性来访问浏览器的原生事件。除了nativeEvent以外,SyntheticEvent类型还提供了以下标准属性可供使用。

  • bubbles:布尔值,用于表示事件是否会冒泡传播。
  • cancelable:布尔值,用于表示事件是否可以被取消。
  • currentTarget:DOM 节点,用来获取当前节点在 React DOM 树中的位置。
  • defaultPrevented:布尔值,用来表示是否调用了preventDefault()方法。
  • eventPhase:数字值,用来表示当前事件所处的阶段。
  • isTrusted:布尔值,用来返回事件是否由用户发起。
  • target:DOM 节点,用来返回事件发生的节点。
  • timestamp:数字值,用来返回事件发生的时间。

常用事件及其对应类型

以下事件监听属性适用于所有内置组件

事件监听属性事件类型触发时间
onAnimationEndAnimationEvent在 CSS 动画完成时
onAnimationIterationAnimationEvent在 CSS 动画的一次迭代结束时
onAnimationStartAnimationEvent在 CSS 动画开始时
onAuxClickAnimationEvent当指针设备上非主要指针按钮被点击时
onBeforeInputInputEvent在可编辑元素的值被修改之前
onBlurFocusEvent在元素失去焦点时
onClickMouseEvent当指针设备上的主按钮被点击时
onCompositionStartCompositionEvent当输入法编辑器开始新的组合会话时
onCompositionEndCompositionEvent当输入法编辑器完成或者取消组合会话时
onCompositionUpdateCompositionEvent在输入法编辑器收到一个新的字符时
onContextMenuMouseEvent在用户尝试打开上下文菜单时
onCopyClipboardEvent在用户尝试将一些内容复制到剪贴板时
onCutClipboardEvent在用户尝试将一些内容剪切到剪贴板时
onDoubleClickMouseEvent在用户双击时
onDragDragEvent在用户拖拽某些元素时
onDragEndDragEvent在用户停止拖拽某些元素时
onDragOverDragEvent在被拖动的元素进入指定放置目标时
onDragStartDragEvent在用户开始拖拽元素时
onDropDragEvent在元素被拖放到有效的目的区域时
onFocusFocusEvent在元素获得焦点时
onGotPointerCapturePointerEvent当元素以变成方式捕获指针时
onKeyDownKeyboardEvent当按键被按下时
onKeyUpKeyboardEvent当按键被释放时
onLostPointerCapturePointerEvent当元素停止捕获指针时
onMouseDownMouseEvent当指针被按下时
onMouseEnterMouseEvent当指针在元素内移动时
onMouseLeaveMouseEvent当指针移动到元素外部时
onMouseMoveMouseEvent当指针改变坐标时
onMouseOutMouseEvent当指针移动到元素外部或者移动到子元素时
onMouseUpMouseEvent当指针释放时
onPointerDownPointerEvent当浏览器取消指针交互时
onPointerEnterPointerEvent当指针在元素内移动时
onPointerLeavePointerEvent当指针移动到元素外部时
onPointerMovePointerEvent当指针改变坐标时
onPointerOutPointerEvent当指针移动到元素外部时
onPointerUpPointerEvent当指针不再活动时
onPasteClipboardEvent当用户尝试从剪贴板粘贴内容时
onScrollEvent当元素被滚动时
onSelectEvent当可编辑元素内部的选择更改后
onTouchCancelTouchEvent当浏览器取消触摸交互时
onTouchEndTouchEvent当一个或者多个触摸点被移除时
onTouchMoveTouchEvent当一个或者多个触摸点发生移动时
onTouchStartTouchEvent当一个或者多个触摸点被放置时
onTransitionEndTransitionEvent当 CSS 过度完成时
onWheelWheelEvent当用户旋转滚轮时

以下事件监听属性适用于<form>元素

事件监听属性事件类型触发时间
onResetEvent当表单被重置时
onSubmitEvent当表单被提交时

以下事件监听属性适用于<dialog>元素

事件监听属性事件类型触发时间
onCancelEvent当用户尝试关闭对话框时
onCloseEvent当对话框已经被关闭时

以下事件监听属性适用于<details>元素

事件监听属性事件类型触发时间
onToggleEvent当用户切换详细信息时

以下事件监听属性适用于<img><iframe><object><embed><link>、SVG 中的<image>元素

事件监听属性事件类型触发时间
onLoadEvent当资源开始加载时
onErrorEvent当资源无法加载时

以下事件监听属性适用于<audio><video>元素

事件监听属性事件类型触发时间
onAbortEvent当资源没有完全加载时
onCanPlayEvent当有足够的数据可以开始播放但没有缓冲到结束时
onCanPlayThroughEvent当有足够的数据且可以播放到结束时
onDurationChangeEvent当媒体持续事件更新时
onEmptiedEvent当媒体变为空时
onEncryptedEvent当浏览器遇到加密媒体时
onEndedEvent当因为没有剩余内容可以播放而停止时
onErrorEvent当资源无法播放时
onLoadedDataEvent在当前播放帧已被加载时
onLoadedMetadataEvent当元数据加载完成时
onLoadStartEvent当浏览器开始加载资源时
onPauseEvent当媒体暂停时
onPlayEvent当媒体不再暂停时
onPlayingEvent当媒体开始或者重新开始播放时
onProgressEvent在资源加载时定期触发
onRateChangeEvent当播放帧率改变时
onResizeEvent当视频大小发生改变时
onSeekedEvent当搜索操作完成时
onSeekingEvent当搜索操作开始时
onStalledEvent当浏览器等待数据时
onSuspendEvent当资源加载被暂停时
onTimeUpdateEvent当播放时间更新时
onVolumeChangeEvent当音量发生变化时
onWaitingEvent当临时缺少数据而播放停止时