ahooks源码分析-useLongPress

1,301 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第18天,点击查看活动详情

今天开始一起学习分享 ahooks 的源码

ahooks是阿里巴巴出品的一套高质量可靠的 React Hooks 库

今天分享 第13个 hooks-useLongPress

useLongPress

首先看下 useLongPress 的作用是什么

useLongPress 的作用是 监听目标元素的长按事件。

接下来 看下 API

API

useLongPress(
  onLongPress: (event: MouseEvent | TouchEvent) => void,
  target: Target,
  options?: {
    delay?: number;
    moveThreshold?: { x?: number; y?: number };
    onClick?: (event: MouseEvent | TouchEvent) => void;
    onLongPressEnd?: (event: MouseEvent | TouchEvent) => void;
  }
);

Params

一共有3个参数

第1个参数是onLongPress,表示触发长按事件的函数

第2个参数是目标元素target,目标元素可以使用dom节点,也可以使用ref来指定dom节点

第3个参数是一些可配置的项

参数说明类型默认值
onLongPress触发函数(event: MouseEvent | TouchEvent) => void-
targetDOM 节点或者 RefElement | () => Element | MutableRefObject<Element>-
options可选配置项Options-

Options

一共有4个配置项

第1个是delay,表示长按的时间

第2个是moveThreshold,表示按下后移动阈值,超出则不触发长按事件

第3个是onClick,表示点击事件

第4个是onLongPressEnd,表示长按结束事件

参数说明类型默认值
delay长按时间number300
moveThreshold按下后移动阈值,超出则不触发长按事件{ x?: number; y?: number }-
onClick点击事件(event: MouseEvent | TouchEvent) => void-
onLongPressEnd长按结束事件(event: MouseEvent | TouchEvent) => void-

备注

禁用在手机上长按选择文本的能力请参考:stackoverflow.com/a/11237968

接下来 看下 用法

基本用法

请长按按钮查看效果。

import React, { useState, useRef } from 'react';
import { useLongPress } from 'ahooks';

export default () => {
  const [counter, setCounter] = useState(0);
  const ref = useRef<HTMLButtonElement>(null);

  useLongPress(() => setCounter((s) => s + 1), ref);

  return (
    <div>
      <button ref={ref} type="button">
        Press me
      </button>
      <p>counter: {counter}</p>
    </div>
  );
};

初始化值是0,当长按的时候数字加1

image.png

同时监听点击和长按事件

import React, { useRef, useState } from 'react';
import { useLongPress } from 'ahooks';

export default () => {
  const [pressCounter, setPressCounter] = useState(0);
  const [clickCounter, setClickCounter] = useState(0);

  const ref = useRef<HTMLButtonElement>(null);

  useLongPress(() => setPressCounter((s) => s + 1), ref, {
    onClick: () => setClickCounter((s) => s + 1),
  });

  return (
    <div>
      <button ref={ref} type="button">
        Press me
      </button>
      <p>pressCounter: {pressCounter}</p>
      <p>clickCounter: {clickCounter}</p>
    </div>
  );
};

image.png

移动阈值

超出移动阈值之后,长按事件将不会触发

import React, { useRef, useState } from 'react';
import { useLongPress } from 'ahooks';

export default () => {
  const [pressCounter, setPressCounter] = useState(0);

  const ref = useRef<HTMLButtonElement>(null);

  useLongPress(() => setPressCounter((s) => s + 1), ref, {
    moveThreshold: { x: 30 },
  });

  return (
    <div>
      <button ref={ref} type="button">
        Press me
      </button>
      <p>counter: {pressCounter}</p>
    </div>
  );
};

接下来一起看下 源码

源码

1.首先定义3个参数的类型

onLongPress: (event: EventType) => void,
  target: BasicTarget,
  { delay = 300, moveThreshold, onClick, onLongPressEnd }: Options = {},
  
  
 type EventType = MouseEvent | TouchEvent;
export interface Options {
  delay?: number;
  moveThreshold?: { x?: number; y?: number };
  onClick?: (event: EventType) => void;
  onLongPressEnd?: (event: EventType) => void;
}

2.使用useLatest Hooks包裹住下面三个函数

const onLongPressRef = useLatest(onLongPress);
  const onClickRef = useLatest(onClick);
  const onLongPressEndRef = useLatest(onLongPressEnd);

3.获取目标元素,如果没有目标元素,则直接return

const targetElement = getTargetElement(target);
      if (!targetElement?.addEventListener) {
        return;
      }

4.看下最后返回的结果,首先判断是否支持touch事件,如果不支持touch事件,则按mouse事件处理,否则按touch事件处理。然后再reteun中销毁事件。如果是Mouse事件,分别mousedown、mouseup、mouseleave事件。如果设置了阈值,则执行mousemove事件

if (!touchSupported) {
        targetElement.addEventListener('mousedown', onStart);
        targetElement.addEventListener('mouseup', onEndWithClick);
        targetElement.addEventListener('mouseleave', onEnd);
        if (hasMoveThreshold) targetElement.addEventListener('mousemove', onMove);
      } else {
        targetElement.addEventListener('touchstart', onStart);
        targetElement.addEventListener('touchend', onEndWithClick);
        if (hasMoveThreshold) targetElement.addEventListener('touchmove', onMove);
      }
return () => {
        if (timerRef.current) {
          clearTimeout(timerRef.current);
          isTriggeredRef.current = false;
        }
        if (!touchSupported) {
          targetElement.removeEventListener('mousedown', onStart);
          targetElement.removeEventListener('mouseup', onEndWithClick);
          targetElement.removeEventListener('mouseleave', onEnd);
          if (hasMoveThreshold) targetElement.removeEventListener('mousemove', onMove);
        } else {
          targetElement.removeEventListener('touchstart', onStart);
          targetElement.removeEventListener('touchend', onEndWithClick);
          if (hasMoveThreshold) targetElement.removeEventListener('touchmove', onMove);
        }
      };

其他hooks

useLatest

useEventListener

useClickAway

useDocumentVisibility

useTitle