ahooks源码分析-useClickAway

342 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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-
targetDOM 节点或者 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>
  );
};

image.png

自定义 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>
  );
};

image.png

监听其它事件

通过设置 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);
      };

其他hooks

useLatest

useEventListener