持续创作,加速成长!这是我参与「掘金日新计划 · 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 | - |
| target | DOM 节点或者 Ref | Element | () => Element | MutableRefObject<Element> | - |
| options | 可选配置项 | Options | - |
Options
一共有4个配置项
第1个是delay,表示长按的时间
第2个是moveThreshold,表示按下后移动阈值,超出则不触发长按事件
第3个是onClick,表示点击事件
第4个是onLongPressEnd,表示长按结束事件
| 参数 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| delay | 长按时间 | number | 300 |
| 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
同时监听点击和长按事件
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>
);
};
移动阈值
超出移动阈值之后,长按事件将不会触发
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);
}
};