Published on

alibaba/hooks代码解读之useClickAway

Authors
  • avatar
    Name
    祝你好运
    Twitter

useClickAway是用来管理点击的,但是这些点击是在目标元素之外,不在之内。啥时候会用到它呢?比如我做了一个弹窗,当点击弹窗外的地方的时候,我想要收起弹窗,这个时候就可以用useClickAway

useClickAway

先看方法的定义:

export default function useClickAway(
  onClickAway: (event: EventType) => void,
  target: BasicTarget | BasicTarget[],
  eventName: string = defaultEvent,
)

这里一共三个参数,

  1. 第一个是一个回调,回调的参数是EventType,它的具体类型是:type EventType = MouseEvent | TouchEvent;
  2. 第二个参数是目标元素,也可以是目标元素的集合
  3. 第三个参数是事件名,默认是click

这里要额外看一下BasicTarget

export type BasicTarget<T = HTMLElement> =
  | (() => T | null)
  | T
  | null
  | MutableRefObject<T | null | undefined>;

我们可以看出BasicTargetT默认是HTMLElement类型的,它可能有4种值,一种是null,一种就是T,还有就是一个无参但是可以返回Tnull的函数,最后一种就是我们经常使用的ref,它的定义就是React.MutableRefObject。简单来说就是BasicTarget是一个很灵活的类型,我们可以从中获取到我们想要的target,当然也有可能为空。

然后是这两行:

  const onClickAwayRef = useRef(onClickAway);
  onClickAwayRef.current = onClickAway;

其实猛一看,它为什么还要用ref呢?如果是我来写的话,我是不会用的。但是再仔细想一想的话,还真是应该用ref,这其实是hooks反直觉的一个地方,onClickAway最开始传进来的时候有可能是一个值,然后用户后面又传进来一个新的值,如果我们不用ref,那我们的onClickAway就一直是第一次传进来的,这不符合需求。

接着是两个大的useEffect,副作用来自targeteventName。这里面的核心就是在document上面加了一个click事件的回调,注意,这里要强调是document,因为如果只加到target上面,那当我们点击到target之外的地方的时候,是收不到回调的。然后,在handler里面我们要去判断,点击的目标是否在target里面,还是在target外面。然后这里稍微写的有些晦涩,是因为target是要支持单个或者多个,而且它是BasicTarget类型的,也就是说值的获取会稍微有些麻烦,判断的时候就需要兼顾所有这些情况。 最后注意这个useEffect里面需要返回一个清除函数,不能忘记。

参考:

  1. hooks
  2. Node.contains()
  3. Functional updates