持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情
今天开始一起学习分享 ahooks 的源码
ahooks是阿里巴巴出品的一套高质量可靠的 React Hooks 库
今天分享 第3个 hooks- useClickAway
useClickAway
首先看下 useClickAway 的作用是什么
useClickAway的作用是监听目标元素外的点击事件。
接下来 看下 API
API
type Target = Element | (() => Element) | React.MutableRefObject<Element>;
useClickAway<T extends Event = Event>(
onClickAway: (event: T) => void,
target: Target | Target[],
eventName?: string | string[]
);
Params
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
onClickAway | 触发函数 | (event: T) => void | - |
target | DOM 节点或者 Ref,支持数组 | Target | Target[] | - |
eventName | 指定需要监听的事件,支持数组 | string | string[] | click |
一共有3个参数
onClickAway:触发函数
target: DOM 节点或者 Ref
eventName:需要监听的事件
接下来 看下 用法
基本用法
点击按钮之外的区域时,数字 加 1
import React, { useState, useRef } from 'react';
import { useClickAway } from 'ahooks';
export default () => {
const [counter, setCounter] = useState(0);
const ref = useRef<HTMLButtonElement>(null);
useClickAway(() => {
setCounter((s) => s + 1);
}, ref);
return (
<div>
<button ref={ref} type="button">
box
</button>
<p>counter: {counter}</p>
</div>
);
};
自定义 DOM
将useClickAway 绑定到 自定义的 dom上,支持直接传入 DOM 对象或 function。
import React, { useState, useRef } from 'react';
import { useClickAway } from 'ahooks';
export default () => {
const [counter, setCounter] = useState(0);
const ref = useRef<HTMLButtonElement>(null);
useClickAway(() => {
setCounter((s) => s + 1);
}, ref);
return (
<div>
<button ref={ref} type="button">
box
</button>
<p>counter: {counter}</p>
</div>
);
};
监听其它事件
通过设置 eventName,可以指定需要监听的事件,试试点击鼠标右键。
import React, { useState, useRef } from 'react';
import { useClickAway } from 'ahooks';
export default () => {
const [counter, setCounter] = useState(0);
const ref = useRef<HTMLButtonElement>(null);
useClickAway(
() => {
setCounter((s) => s + 1);
},
ref,
'contextmenu',
);
return (
<div>
<button ref={ref} type="button">
box
</button>
<p>counter: {counter}</p>
</div>
);
};
接下来 看下 源码
源码
首先看下接口参数的定义
<T extends Event = Event>(
onClickAway: (event: T) => void,
target: BasicTarget | BasicTarget[],
eventName: string | string[] = 'click',
)
第一个是onClickAway, 触发的函数,类型是Event
第二个参数是target,这里定义了 target BasicTarget 或者 BasicTarget[],支持数组
第三个参数是eventName,表示事件名称
1.首先通过useLatest 来包裹 onClickAway,返回最新的函数
const onClickAwayRef = useLatest(onClickAway);
2.然后通过useEffectWithTarget来返回封装的上述3个参数
useEffectWithTarget
3.首先看下第2个参数,第二个参数是事件名称,事件名称支持两种格式,一个是字符串,另外一个是数组
Array.isArray(eventName) ? eventName : [eventName],
4.再看下第三个参数,第三个参数就是 要绑定的 目标target,我们之间返回就行
target
5.最后看下封装的函数,通过箭头函数来执行
() => {}
6.然后定义事件名称的格式,判断事件名称传入的是否是数组,如果是数组,直接使用,如果 不是数组,就将传入的事件名称 包裹成数组
const eventNames = Array.isArray(eventName) ? eventName : [eventName];
7.通过上面获取到了 所有的事件,然后遍历这个事件数组函数,通过 addEventListener来绑定函数,通过removeEventListener来销毁函数
eventNames.forEach((event) => documentOrShadow.addEventListener(event, handler));
return () => {
eventNames.forEach((event) => documentOrShadow.removeEventListener(event, handler));
};
8.最后再看下 是如何处理函数 handler。首先将目标值包裹成数组,判断这个目标值里面的数据 是否是合格的dom,如果不是直接return,如果是则执行该函数
const handler = (event: any) => {
const targets = Array.isArray(target) ? target : [target];
if (
targets.some((item) => {
const targetElement = getTargetElement(item);
return !targetElement || targetElement.contains(event.target);
})
) {
return;
}
onClickAwayRef.current(event);
};