react-custom-hook-three


useDebounce

import { useEffect, useRef, useState } from "react";

// 新debounce的实现更倾向于原生js的debounce

const useDebounce = <V>(value: V, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState(value);
  const timerRef = useRef<NodeJS.Timeout | undefined>(undefined);

  useEffect(() => {
    if (timerRef.current) {
      clearTimeout(timerRef.current);
    }
    timerRef.current = setTimeout(() => {
      setDebouncedValue(value);
    }, delay);

    return () => {
      clearTimeout(timerRef.current);
    };
  }, [value, delay, timerRef.current]);

  return debouncedValue;
};

export default useDebounce;

// 原来的debounce更加清晰明了

const useDebounce = <V>(value: V, delay: number) => {
  const [debouncedValue, setDebouncedValue] = useState<V>(value);

  useEffect(() => {
    // 每次在value变化以后设置一个定时器
    const timeout = setTimeout(() => setDebouncedValue(value), delay);

    // 每次在上一个的effect执行完后执行
    return () => clearTimeout(timeout);
  }, [delay, value]);

  return debouncedValue;
};

useOffsetTop

import { useEffect, useRef, useState } from 'react';
import { useFuncDebounce, useMount } from '.';

interface OffsetProps {
  extraHeight?: number;
  id?: string;
  patch?: number;
}

interface DomAttr {
  top: number;
  bottom: number;
  height: number;
  left: number;
  right: number;
  width: number;
  x: number;
  y: number;
}
export const useOffsetTop = ({ extraHeight = 80, id, patch = 0 }: OffsetProps = {}) => {
  const tableRef = useRef<HTMLDivElement>(null);
  const [height, setHeight] = useState<number>(0);

  const onSizeChange = function () {
    return saveHeightChange();
  };
  const debouncedCallback = useFuncDebounce(onSizeChange, 500);
  const getOffsetTop = () => {
    if (tableRef.current && tableRef.current instanceof HTMLDivElement) {
      const { top } = tableRef.current?.getBoundingClientRect() as DomAttr;
      id ? tableRef.current.setAttribute('data-key', id) : null;
      const tableHeight = document.body.clientHeight - top - extraHeight - patch;
      return tableHeight;
    }

    return 0;
  };

  const exportHeight = () => {
    return height;
  };
  const saveHeightChange = () => {
    return setHeight(() => {
      const height = getOffsetTop() >= 0 ? getOffsetTop() : 0;
      return height;
    });
  };
  addEventListener('resize', debouncedCallback);
  useMount(
    () => {
      saveHeightChange();
    },
    () => {
      removeEventListener('resize', debouncedCallback);
    },
  );
  useEffect(() => {
    saveHeightChange();
  });
  return [tableRef, exportHeight] as const;
};
// 新版本

import { useMemo, useRef, useState } from 'react';
import { useMount } from '.';

interface OffsetProps {
  extraHeight?: number;
  id?: string;
}

interface DomAttr {
  top: number;
  bottom: number;
  height: number;
  left: number;
  right: number;
  width: number;
  x: number;
  y: number;
}

const BODYELEMENT = 'body';
const body = document.getElementsByTagName(BODYELEMENT)[0];

export const useOffsetTop = ({ extraHeight = 80, id }: OffsetProps = {}) => {
  const tableRef = useRef<HTMLDivElement>(null);
  const [height, setHeight] = useState<number>(0);

  const getOffsetTop = () => {
    if (tableRef.current && tableRef.current instanceof HTMLDivElement) {
      const { top } = tableRef.current?.getBoundingClientRect() as DomAttr;
      id ? tableRef.current.setAttribute('data-key', id) : null;
      const tableHeight = document.body.clientHeight - top - extraHeight;
      return tableHeight;
    }

    return 0;
  };

  const exportHeight = () => {
    return height;
  };
  const saveHeightChange = () => {
    return setHeight(() => {
      const height = getOffsetTop() >= 0 ? getOffsetTop() : 0;
      return height;
    });
  };

  const resizeObserver = new ResizeObserver(function (entry) {
    saveHeightChange();
  });

  useMemo(() => {
    resizeObserver.observe(body);
  }, [body, resizeObserver.observe]);

  useMount(
    () => {
      saveHeightChange();
    },
    () => {
      resizeObserver.disconnect();
    },
  );
  return [tableRef, exportHeight] as const;
};

useFuncDebounce

import { useEffect, useRef } from 'react';
import _ from 'lodash';

export const useFuncDebounce = function () {
  const timerRef = useRef<NodeJS.Timeout | undefined>(undefined);

  function debouncedCallback<T extends Function, K extends unknown>(callback: T, delay: number) {
    return function (object: K, ...args: unknown[]) {
      if (timerRef.current) clearTimeout(timerRef.current);
      timerRef.current = setTimeout(() => {
        if (_.isObject(object)) callback.apply(object, args);
        else callback(object, ...args);
      }, delay);
    };
  }
  useEffect(function () {
    return function () {
      clearTimeout(timerRef.current);
    };
  }, []);
  return debouncedCallback;
};

useData

// 可以方便安全的获取数据

import { useState } from 'react';
import _ from 'lodash';

const DATALAYERS = 'data.items';
const TOTALLAYERS = 'data.meta.total';
const MESSAGE = '请求的返回体为空!';

export const useData = <T>() => {
  const [data, setData] = useState<T>();
  const [total, setTotal] = useState<number>(0);

  const setDataAuxiliary = (res: any, path?: string) => {
    if (res) setData(_.get(res, path ? path : DATALAYERS, []));
    else throw new Error(MESSAGE);
  };
  const setTotalAuxiliary = (res: any, path?: string) => {
    if (res) setTotal(_.get(res, path ? path : TOTALLAYERS, 0));
    else throw new Error(MESSAGE);
  };

  return [data, setDataAuxiliary, total, setTotalAuxiliary] as const;
};

useBoolean

import { useState } from 'react';

export const useBoolean = (initValue: boolean = false) => {
  const [value, setValue] = useState(initValue);

  const on = () => {
    setValue(true);
  };
  const off = () => {
    setValue(false);
  };
  const toggle = () => {
    setValue(!value);
  };

  return [value, toggle, on, off] as const;
};

文章作者: KarlFranz
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 reprint policy. If reproduced, please indicate source KarlFranz !
评论
  目录