说不定能派上用场的几个自定义hook

222 阅读3分钟

一 前言

  • 自定义hook是什么?
  • 为什么要封装hook呢?
   自定义hook,也就是hook的封装。
   使用 Hook 其中一个就是要解决 class 中生命周期函数经常包含不相关的逻辑,但又把相关逻辑分离到了几个不同方法中的问题。 通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。

二 介绍几个常用的hook

useRouterSearch:路由参数的获取与更改


  const searchParams: any = new URLSearchParams(url);
  const keys: any[] = [];
  const _setKeys = () => {
    keys.length = 0;
    for (let b of searchParams) {
      keys.push(b[0]);
    }
  };
  _setKeys();
  const setParams = (params: any) => {
    for (let k in params) {
      searchParams.set(k, params[k]);
    }
    _setKeys();
  };
  const getParam = (key: string) => {
    return searchParams.get(key);
  };
  const removeParam = (key: string) => {
    searchParams.delete(key);
  };
  const getParamsString = (): any => {
    return searchParams.toString();
  };
  return { searchParams, setParams, getParamsString, getParam, removeParam };
};
  • searchParams:获取由url?后拼接的key,value组成的对象
    url:pagesize=5&page=1&orderBy=id&order=asc&selected=123465
    searchParams={pagesize:5,page:1,orderBy:id,order:asc,selected:123465}
  • setParams:可以根据特定的key,发生改变时, 重新编辑 URLSearchParams 并生成新的 string push到url。
  • getParamsString:获取URL?后面的值
  • getParam:可以传key拿value
  • removeParam:传key移除某一项

业务场景:可以根据导出几个成员结合使用,生成想要的url,作为新的url去跳转。

useLocalstorage:设置本地存储与读取

 export default (key: string, initialValue: any) => {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);

      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.log(error);
      return initialValue;
    }
  });

  const setValue = (value: any) => {
    try {
      const valueToStore = value instanceof Function ? value(storedValue) : value;
      setStoredValue(valueToStore);
      window.localStorage.setItem(key, JSON.stringify(valueToStore));
    } catch (error) {
      console.log(error);
    }
  };
  return [storedValue, setValue];
};

useDebounce:防抖

export default (value: any, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState(value);

  useEffect(() => {
    const handler = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);
    return () => {
      clearTimeout(handler);
    };
  }, [value, delay]);

  return debouncedValue;
};

useListenScreenSize:获取窗口宽高

首先,先给大家讲一下几个窗口尺寸(scrollWidth、clientWidth、offsetWidth)

  • scrollWidth:是对象的实际内容的宽,不包边线宽度,会随对象中内容的多少改变(内容多了可能会改变对象的实际宽度)。
  • clientWidth:是对象可见的宽度,不包滚动条等边线,会随窗口的显示大小改变
  • offsetWidth:是对象的可见宽度,包滚动条等边线,会随窗口的显示大小改变。
  const [size, setSize] = useState({
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight
  });

  const onResize = useCallback(() => {
    setSize({
      width: document.documentElement.clientWidth,
      height: document.documentElement.clientHeight
    });
  }, []);

  useEffect(() => {
    window.addEventListener('resize', onResize);
    return () => {
      window.removeEventListener('resize', onResize);
    };
  }, []);

  return size;
};

useFullScreen:全屏

import { useCallback, useEffect, useState } from 'react';

export default (id: string) => {
  const [isFullPage, setIsFullPage] = useState<boolean>(false);

  /**
   * 点击触发全屏
   */
  const onFullPageOpen = useCallback(() => {
    const el = document.getElementById(id) as any;
    let rfs = el.requestFullScreen || el.webkitRequestFullScreen || el.mozRequestFullScreen || el.msRequestFullscreen;
    if (rfs) {
      rfs.call(el);
      setIsFullPage(true);
    } else {
      console.error('当前浏览器不兼容');
    }
  }, []);

  /**
   * 关闭全屏
   */
  const onFullPageClose = useCallback(() => {
    let el = document as any;
    let cfs =
      el.cancelFullScreen ||
      el.mozCancelFullScreen ||
      el.msExitFullscreen ||
      el.webkitExitFullscreen ||
      el.exitFullscreen;
    if (cfs) {
      cfs.call(el);
      setIsFullPage(false);
    } else {
      console.error('当前浏览器不兼容');
    }
  }, []);

  /**
   * 监听是否是全屏状态 f11 事件
   */
  useEffect(() => {
    bindFullscreenListener();
    return () => unBindFullscreenListener();
  }, []);

  const bindFullscreenListener = () => {
    // 监听退出全屏事件 --- chrome 用 esc 退出全屏并不会触发 keyup 事件
    document.addEventListener('webkitfullscreenchange', checkFull);
    document.addEventListener('mozfullscreenchange', checkFull);
    document.addEventListener('fullscreenchange', checkFull);
    document.addEventListener('MSFullscreenChange', checkFull);
  };

  const unBindFullscreenListener = () => {
    document.removeEventListener('webkitfullscreenchange', checkFull);
    document.removeEventListener('mozfullscreenchange', checkFull);
    document.removeEventListener('fullscreenchange', checkFull);
    document.removeEventListener('MSFullscreenChange', checkFull);
  };

  /**
   * 如果是 esc 关闭了全屏事件,显示全屏按钮,隐藏取消全屏按钮
   */
  const checkFull = () => {
    const el = document as any;
    if (!el.webkitIsFullScreen && !el.mozFullScreen && !el.msFullscreenElement) {
      setIsFullPage(false);
    }
  };

  return { isFullPage, onFullPageOpen, onFullPageClose };
};

注意

需要注意的是hook的封装函数必须要以use开头,因为使用hook本身是有规则的,比如不能在条件语句中使用hook,不能在函数外使用hook;如果不适用use开头封装hook,则react无法自动检查该函数内使用的hook是否符合规则。

最后

这里只是分享几个可能会用到的hook。在日常开发中,大家也可以尝试着根据具体业务把逻辑提取到可重用的函数中,这样在组件中直接用调用自定义hook就会方便很多。使得在后面的开发中遇到相同业务逻辑可以直接复用,大大提高了效率。