react-custom-hook-one


react custom hook

useBoolean

import {useState, useCallback} from 'react';

type BooleanHook = [boolean, () => void, () => void];

const useBoolean = (initialValue: boolean = false): BooleanHook => {
    const [value, setValue] = useState(initialValue);
    const on = useCallback(
        () => setValue(true),
        []
    );
    const off = useCallback(
        () => setValue(false),
        []
    );

    return [value, on, off];
};

export default useBoolean;

useQuery

import {useMemo} from 'react';
import {useLocation} from 'react-router-dom';
import * as queryString from 'query-string';

type ParsedQuery = queryString.ParsedQuery;

export const useQuery = <T extends ParsedQuery>(): T => {
    const {search} = useLocation();
    const query = useMemo(
        () => queryString.parse(search, {arrayFormat: 'bracket'}),
        [search]
    );
    return query as T;
};

export default useQuery;

useCountDown one

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

// 入参
export interface Countdown {
  deadline: string;
  format?: 'YYYY-MM-DD HH:mm:ss' | string;
}
// 返回值
export type Remains = Record<'day' | 'hour' | 'minute' | 'second', number>;

const useCountdown = ({
    deadline,
    format = 'YYYY-MM-DD HH:mm:ss',
}: ICountdown): Remains => {
    // 由于 moment() 返回对象,setCurrent 修改值后指针不变,无法在 useEffect 中捕获变化,所以这里定义了一个 updater 用于 useEffect 捕获时间更新
    const [{current, updater}, setCurrent] = useState({
        current: moment(),
        updater: 0,
    });

    const [remains, setRemains] = useState<Remains>({
        day: 0,
        hour: 0,
        minute: 0,
        second: 0,
    });

    useEffect(
        () => {
            const timer = window.setInterval(() => {
                current.isSameOrAfter(moment(deadline, format))
                    ? clearInterval(timer)
                    : setCurrent(prev => ({
                        current: prev.current.add(0.5, 's'),
                        updater: prev.updater + 1,
                    }));
            }, 1000);
            return () => clearInterval(timer);
        },
        [deadline]
    );

    const formatTime = useCallback(
        time => (
            time < 10 ? `0${time}` : time
        ),
        []
    );


    // current 变化,计算相差多长时间
    useEffect(
        () => {
            let millisec = moment(deadline, format).valueOf() - current.valueOf();
            // 处理 millisec 可能为负数的情况
            millisec = millisec >= 0 ? millisec : 0;
            // 用毫秒数得到秒、分、小时和天
            setRemains({
                day: formatTime(Math.floor(millisec / (1000 * 60 * 60 * 24))),
                hour: formatTime(Math.floor((millisec / (1000 * 60 * 60)) % 24)),
                minute: formatTime(Math.floor((millisec / (1000 * 60)) % 60)),
                second: formatTime(Math.round((millisec / 1000) % 60)),
            });
        },
        [updater]
    );

    return remains;
};

export default useCountdown;
  
// use 解构使用  data  ---> props

const deadline = useMemo(
  () =>
      moment(data?.ctime * 1000).add(5, 'm').format('YYYY-MM-DD HH:mm:ss'),
  [data?.ctime]
);
 
const {hour, minute, second} = useCountdown({
    deadline,
});

useCountDown two

import React , {useState, useEffect, useCallback} from "react";

/* 
*补位
* param n 数值     
*/
// 时间补位方法

const bw = (n: number) => {
    return n > 9 ? n :'0' + n;
};

interface CounrDown {
    expried: boolean,
    timeStr: string,
    days: number,
    hours: number,
    minutes: number,
    seconds: number,
}

const useCountDown = (targetTime: string | number) => {// 可以定义多种数据格式,可能需要自己转换
    const [timeDiff, setTimeDiff] = useState<CounrDown>({
        expried: false,
        timeStr: '',
        days: 0,
        hours: 0,
        minutes: 0,
        seconds: 0,
    });

    const time = useCallback(() => {
        // 未来时间
        let future = + new Date(targetTime) // + new Date() 转为时间戳的另一种方式
        // 未来时间-现在时间 = 时间戳
        // 现在时间
        let nowDate = + new Date()

        let s = parseInt((future-nowDate) / 1000 + '')//转化为秒

        // * 天
        let d = parseInt(s / 86400 + '')

        // # 小时
        let h = parseInt((s % 86400) / 3600 + '')

        // todo 分钟
        let ms = parseInt((s % 3600) / 60 + '')

        // ? 秒
        let sc = parseInt(s % 60 + '')
        
        // const timeStr =  '距离2020年12月12日12分12秒还剩:'+bw(d)+'天-'+bw(h)+'小时-'+bw(ms)+'分钟-'+bw(sc)+'秒';
        const timeStr =  `距离${targetTime}还剩:${bw(d)}天-${bw(h)}小时-${bw(ms)}分钟-${bw(sc)}秒`;

        const expried = s > 0 ? false : true;


        // ? why use setTimeout can be ? 位于 effect 后被 effect 监听到

        // # asyncHronous task can not exceed max update depth , so this is important !
        setTimeout(() => {
            setTimeDiff(state => {
                return {// 每次都是全新的对象
                    ...state,
                    timeStr,
                    expried,
                    days: d < 0 ? - d : d, // 无论是正数还是负数,一律转化为正整数
                    hours: h < 0 ? - h : h,
                    minutes: ms < 0 ? - ms : ms,
                    seconds: sc < 0 ? - sc : sc,
                }
            });
        }, 1000)
    }, 
        [timeDiff]
    );

    useEffect(() => {
        time();
    }, [time]);

    return timeDiff;

}

export default useCountDown;


// use 解构使用

const result = useCountDown(targetTime);

useInterval

// auth danny

/**
* react hook 和定时器的结合使用, 可用于下面的 useCountDown hook
*
* 这是一个可以控制速度、暂停的
*
* 神奇的 hook
*/

import React ,{useRef, useEffect, MutableRefObject} from "react";

const useInterVal = (callback: () => void, delaty: number | null) => {
    const savedCallback: MutableRefObject<any>= useRef();

    useEffect(() => {
        savedCallback.current = callback;
    });

    useEffect(() => {
        const tick = () => {
            savedCallback.current();
        };

        // 暂停
        if (delaty !== null) {
            const id = setInterval(tick, delaty);
            return () => clearInterval(id);
        }
    }, [delaty]);
};

export default useInterVal;

useCountDown for two edit

// 依赖上面 useInterVal hook 
import React , {useState} from "react";
import useInterVal from "./useInterval";

/* 
*补位
* param n 数值     
*/
// 时间补位方法

const bw = (n: number) => {
    return n > 9 ? n :'0' + n;
};

interface CounrDown {
    expried: boolean,
    timeStr: string,
    days: number,
    hours: number,
    minutes: number,
    seconds: number,
}

const useCountDown = (targetTime: string | number) => {// 可以定义多种数据格式,可能需要自己转换
    const [timeDiff, setTimeDiff] = useState<CounrDown>({
        expried: false,
        timeStr: '',
        days: 0,
        hours: 0,
        minutes: 0,
        seconds: 0,
    });

    const time = () => {
        // 未来时间
        let future = + new Date(targetTime) // + new Date() 转为时间戳的另一种方式
        // 未来时间-现在时间 = 时间戳
        // 现在时间
        let nowDate = + new Date()

        let s = parseInt((future-nowDate) / 1000 + '')//转化为秒

        // * 天
        let d = parseInt(s / 86400 + '')

        // # 小时
        let h = parseInt((s % 86400) / 3600 + '')

        // todo 分钟
        let ms = parseInt((s % 3600) / 60 + '')

        // ? 秒
        let sc = parseInt(s % 60 + '')
        
        const timeStr =  `距离${targetTime}还剩:${bw(d)}天-${bw(h)}小时-${bw(ms)}分钟-${bw(sc)}秒`;
        const expried = s > 0 ? false : true;
        const days = d < 0 ? - d : d;
        const hours = h < 0 ? - h : h;
        const minutes = ms < 0 ? - ms : ms;
        const seconds = sc < 0 ? - sc : sc;

        setTimeDiff(state => {
            state.timeStr = timeStr;
            state.expried = expried;
            state.days = days;
            state.hours = hours;
            state.minutes = minutes;
            state.seconds = seconds;
            return state;
        });
    };

    useInterVal(() => {
        time()
    }, 1000); // 可传变量控制速度

    return timeDiff;

}

export default useCountDown;

// use  同解构使用

useSyncCallback (在回调中获得useState最新的值)

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

const useSyncCallback = <T>(callback: (value?: T) => void) => {
    const [proxyState, setProxyState] = useState({current: false});
    const [parameters, setParameters] = useState<T>();

    const Func = useCallback((val?: T) => {
        setParameters(val);
        setProxyState({current: true});
    }, []);

    useEffect(() => {
        if (proxyState.current === true) {
            setProxyState({current: false});
        }
    }, [proxyState]);

    useEffect(() => {
        proxyState.current && callback(parameters);
    });

    return Func;
};

export default useSyncCallback;
// use 
const getSyncHot = useSyncCallback(() => {
    // 在此函数内部 为 '同步'
    const belongList = searchData.status || searchData.status === 0 ? searchData.status : '';
    const status = searchData.time || searchData.time === 0 ? searchData.time : '';
    const sendParams = {
        belongList,
        content: searchData.content,
        page: 1,
        pageOrder: true,
        pageSize: 10,
        pageSort: 'create_time',
        regionCode: searchData.typeKeywords.regionCode || '',
        status,
    };

    (async () => {
            ...
    })();
});

useStateSync (同步state hook)

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

enum NUMS {
  ZERO,
  ONE,
}

type SyncFunc<K> = (value: K) => void | undefined;
type DispatchFunc<K> = (newData: K | ((prev: K) => K), callF?: SyncFunc<K>) => Promise<K>;
type ReturnArray<K> = [K, DispatchFunc<K>];

const useStateSync = <T>(initValue: T) => {
  const ref = useRef<number>(NUMS.ZERO);
  const callFRef = useRef<(val: T) => void>();
  const setFuncRef = useRef<DispatchFunc<T>>();
  const [state, setState] = useState<T>(initValue);
  const prevValue = useRef<T>(initValue);

  if (!ref.current) {
    ref.current = NUMS.ONE;
    setFuncRef.current = (newData: T | ((prev: T) => T), callF?: SyncFunc<T>) => {
      callFRef.current = callF;
      if (newData instanceof Function) {
        setState((prev) => {
          prevValue.current = prev;
          return newData(prev);
        });
        return Promise.resolve(prevValue.current);
      }
      setState(newData);
      return Promise.resolve(newData);
    };
  }
  useEffect(() => {
    callFRef.current?.(state);
  }, [state]);
  // eslint-disable-next-line prettier/prettier
  return [state, setFuncRef.current] as ReturnArray<T>;
};

export default useStateSync;
// use


  const [commonNum, setCommonNum] = useStateSync<
    {
      key: number;
      fileList: unknown[];
    }[]
  >(commonNumData);

  const customRequest = ({ file, onSuccess }: any, index: number) => {
    setTimeout(() => {
      onSuccess('success');
      setCommonNum( // 第一个参数可以传一个值,也可以传一个函数,函数计算的结果作为改变后的值
        (prevCommonNum) => upDateFileList(prevCommonNum, index, file), 
        (newValue) => console.log('newValue', newValue), // 最新一次渲染的值
      ).then((res) => console.log('reeessss', res)); // 最新一次渲染的值
      console.log('立即查看', commonNum); // 过去渲染函数的值
    }, 1000);
  };

// ----------------------
const [count, setCount] = useStateSync<number>(1);

const handelClick = () => {
    // setCount(count + 1, (newState: any) => {
    // 在该函数体内可以获得最新的值
    //     console.warn('syncState', newState);
    // });
    // 在 .then 内也可以获得最新的值
    setCount(count + 1).then((newValue: number) => {
        console.warn('.thenSyncState', newValue);
    });
    console.warn('noSync', count);
};

useMount ( 只执行一次)

// 这个hook的作用在于 只执行一次 避免写大量的空数组的effect
import { useEffect } from "react";

export const useMount = (callBack: () => void) => {
  useEffect(() => {
    callBack();
  }, []);
};

useDebounce (对值进行防抖)

import { useEffect, useState } from "react";

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

  useEffect(() => {
    // 每次在value变化以后设置一个定时器
    let timeout = setTimeout(() => setDebouncedValue(value), delay);
    // 每次在上一个的effect执行完后执行
    return () => clearTimeout(timeout);
  }, [delay, value]);

  return debouncedValue;
};
  // use
  ...
  ..
  
  const [param, setParam] = useState({
    name: "",
    personId: "",
  });

  const debouncedParam = useDebounce(param, 500); // 对值进行debounce,只取最后结果

  useEffect(() => {
    fetch(`${api}/projects?${qs.stringify(cleanObject(debouncedParam))}`).then(async (response) => {
      if (response.ok) {
        setList(await response.json());
      }
    });
  }, [debouncedParam]); // 依赖项不再是频繁变化的 param, 从而在input输入阶段不再发送大量请求,只发送最后一次!

useMountRef 判断组件处于挂载还是卸载状态

import { useEffect, useRef } from "react"

export const useMountRef = () => {
    const mountedRef = useRef(false);
    useEffect(() => {
        mountedRef.current = true;
        return () => {
            mountedRef.current = false;
        }
    });

    return mountedRef;
};

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