跟着 ahooks 写 Hook 之 useFullscreen、useFocusWithin、useHover

293 阅读1分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

💻文档地址:ahooks.js.org/zh-CN/

👨‍💻github地址:github.com/alibaba/hoo…


useFullscreen

这个 hooks 中,用到了 screenfull 库。

hooks 向外暴露当前是否全屏的状态 state,以及 enterFullscreenexitFullscreentoggleFullscreen 三个方法。

enterFullscreen

判断当前是否存在 el。判断 screenfull.isEnabled 是否为 true(支持全屏),如果为 true,则调用 screenfull 的 request() 方法。

exitFullscreen

调用 screenfull 的 exit() 方法。

const useFullscreen = (target: BasicTarget, options?: Options) => {
  const [state, setState] = useState<boolean>(false);

  const enterFullscreen = () => {
    const el = getTargetElement(target);
    if (!el) {
      return;
    }

    if (screenfull.isEnabled) {
      try {
        screenfull.request(el);
      } catch (error) {
        console.error(error);
      }
    }
  };

  const exitFullscreen = () => {
    if (screenfull.isEnabled) {
      screenfull.exit();
    }
  };

  return [
    state,
    {
      enterFullscreen: useMemoizedFn(enterFullscreen),
      exitFullscreen: useMemoizedFn(exitFullscreen),
      isEnabled: screenfull.isEnabled,
    },
  ] as const;
};

export default useFullscreen;

触发函数之后的回调,onExitRefonEnterRef

screenfull.off('change', callback);
const onChange = () => {
  if (screenfull.isEnabled) {
    const { isFullscreen } = screenfull;
    if (isFullscreen) {
      onEnterRef.current?.();
    } else {
      // screenfull.off('change', onChange);
      onExitRef.current?.();
    }
    setState(isFullscreen);
  }
};

const enterFullscreen = () => {
  if (screenfull.isEnabled) {
    try {
      screenfull.on('change', onChange);
    }
  }
};

useFocusWithin

监听当前焦点是否在某个区域之内,同 css 属性 :focus-within

:focus-within 是一个CSS 伪类 ,表示一个元素获得焦点

/* 当 <div> 的某个后代获得焦点时,匹配 <div> */
h2:focus-within {
  background: red;
}
<form>
  <label for="given_name">Given Name:</label>
  <input id="given_name" type="text">
  <br>
  <label for="family_name">Family Name:</label>
  <input id="family_name" type="text">
</form>
form {
  border: 1px solid;
  color: gray;
  padding: 4px;
}

form:focus-within {
  background: #ff8;
  color: black;
}

input {
  margin: 4px;
}

图片.png

传入 target,监听 focusinfocusout 事件。向外触发 onFocusonChange 事件,并且设置当前 focus 的状态为 true or false

export default function useFocusWithin(target: BasicTarget, options?: Options) {
  const [isFocusWithin, setIsFocusWithin] = useState<boolean>(false);
  const { onFocus, onBlur, onChange } = options || {};

  useEventListener(
    'focusin',
    (e: FocusEvent) => {
      if (!isFocusWithin) {
        onFocus?.(e);
        onChange?.(true);
        setIsFocusWithin(true);
      }
    },
    {
      target,
    },
  );

  useEventListener(
    'focusout',
    (e: FocusEvent) => {
      if (isFocusWithin && !(e.currentTarget as Element)?.contains?.(e.relatedTarget as Element)) {
        onBlur?.(e);
        onChange?.(false);
        setIsFocusWithin(false);
      }
    },
    {
      target,
    },
  );

  return isFocusWithin;
}

useHover

监听 DOM 元素是否有鼠标悬停

传入 tagret,监听 mouseentermouseleave 事件。向外触发 onEnter 或者 onLeave 或者 onChange 事件。

export default (target: BasicTarget, options?: Options): boolean => {
  const { onEnter, onLeave, onChange } = options || {};

  const [state, { setTrue, setFalse }] = useBoolean(false);

  useEventListener(
    'mouseenter',
    () => {
      onEnter?.();
      setTrue();
      onChange?.(true);
    },
    {
      target,
    },
  );

  useEventListener(
    'mouseleave',
    () => {
      onLeave?.();
      setFalse();
      onChange?.(false);
    },
    {
      target,
    },
  );

  return state;
};