ahooks 源码实现

ahooks库源码实现
state模块

  1. useSetState
  2. 功能点:
    1. 实现类似class组件中 setState功能,只更新传入的值,其他值不用更新;
    2. 且可以穿入第二个回调函数 参数同步获取更新后的最新state用于操作。
    import { useState } from 'react';

export const useSetState = (init = {}) => {
const [state, setState] = useState(init);
const hanleSetState = (value, callback) => {
let newState;
setState((oldState) => {
const v = typeof value === 'function' ? value(oldState) : value;
newState = { ...oldState, ...v };
callback?.(newState);
return newState;
});
};
return [state, hanleSetState];
};
2. useBoolean

  1. 功能点:
    1. 用于设置boolean类型
    import { useState } from 'react';

export const useBoolean = (init = false) => {
const [state, setState] = useState(init);
const set = (v: boolean) => {
setState(v);
};
const toggle = () => {
setState((v) => !v);
};
const setTrue = () => {
setState(true);
};
const setFalse = () => {
setState(false);
};
return [state, { toggle, set, setTrue, setFalse }];
};

  1. useToggle
  2. 功能点
    1. 用于设置两个状态之间相互转化;
    import { useState, useMemo } from 'react';

type Actions = {
set: (v: T) => void;
setLeft: () => void;
setRight: () => void;
toggle: () => void;
};
interface UseToggleType {
(): [state: boolean, actions: Actions];
(defaultValue: T): [state: T, actions: Actions];
<T, U>(defaultValue: T, reverseValue: U): [state: T, actions: Actions<T | U>];
}
export const useToggle: UseToggleType = <T, U>(
defaultValue: T = true as unknown as T,
reverseValue?: U,
): [state: T | U, actions: Actions] => {
const [state, setState] = useState<T | U>(defaultValue);
const actions = useMemo(() => {
const reverseValueOrigin = (reverseValue === undefined ? !defaultValue : reverseValue) as U;
return {
toggle: () => {
setState((oldState) => {
return oldState === defaultValue ? reverseValueOrigin : defaultValue;
});
},
set: (v: T | U) => {
if ([defaultValue, reverseValue].includes(v)) {
setState(v);
}
},
setLeft: () => {
setState(() => {
return defaultValue;
});
},
setRight: () => {
setState(() => {
return reverseValueOrigin;
});
},
};
// 思考,这儿应该不用去依赖defaultValue 和 reverseValue,因为初始化hook时穿参,后续也不会再变化
}, []);
return [state, actions];
};

  1. useLocalStorageState
    (同useSessionStorageState,useCookieStorageState)
  2. 功能点:存储数据到localStorage中,关闭页面后 下次打开页面能够从local中拿到数据进行初始化。
  3. 使用说明
    1. 适合于state 和 localstorage的结合,用于数据缓存,并且将缓存的数据作为 state 用于视图的更新。
    2. 比如,树结构的宽度值,可以使用此hook。
  4. 配置参数
    [图片]
    import { useState, useEffect } from 'react';

export const useLocalStorageState = (
key: string,
value?: { defaultValue: T },
serializer: (value: any) => string = (v) => JSON.stringify(v),
deserializer: (value: string) => any = (v) => JSON.parse(v),
): [state: T, setMessage: (v: T) => void] => {
const [state, setState] = useState();
useEffect(() => {
if (value?.defaultValue) {
setMessage(value.defaultValue);
} else {
const defaultValue = localStorage.getItem(deserializer(key)) as T;
setState(defaultValue);
}
}, []);
const setMessage = (v: T) => {
localStorage.setItem(key, serializer(v));
setState(v);
};
return [state, setMessage];
};
5. useDebounce

  • 作用:用来监控某个值 state 或者 props的值改变,从而影响其他值 按照防抖的标准来改变的情况。

import { useState, useEffect, useMemo } from 'react';
import { debounce } from 'lodash';

// 防抖函数
// 本质是利用 闭包来存储 定时器变量,返回的函数每次执行都会先去检查定时器变量是否已经执行过了定时器,否则先清除定时器;
function debounce(fn: (...arg: any[]) => any, wait: number = 1000) {
let timer = null;
return (...arg: any[]) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn(...arg);
clearTimeout(timer);
}, wait);
};
}

export const useDebounce = (value: T, options: { wait: number }) => {
const [message, setMessage] = useState(value);
const debounceFun = useMemo(
() =>
debounce((v: T) => {
setMessage(v);
}, options.wait),
[],
);
useEffect(() => {
console.log('value changed', value);
debounceFun(value);
}, [value]);
return message;
};

  1. useThrottle
  • 节流hook
  • 作用:某个stage或者props改变,某个值需要按照节流的标准进行改变的情况
    import { useState, useEffect, useMemo } from 'react';
    import { throttle } from 'lodash';

// 节流函数
function throttle(fn, wait) {
let timerNow = performance.now();
return (...arg: any[]) => {
const timerFn = performance.now();
if (timerFn - timerNow >= wait) {
fn(...arg);
timerNow = timerFn;
}
};
}

export const useThrottle = (value: T, options: { wait: number }) => {
const [message, setMessage] = useState();
const throttleFn = useMemo(() => {
return throttle((v: T) => {
setMessage(v);
}, options.wait);
}, []);
useEffect(() => {
throttleFn(value);
}, [value]);
return message;
};
7. useMap

  • 作用: state 需要使用map数据结构。 用于map类型的state封装了几个操作map数据类型的方法。
  • 回顾:Map 数据类型的几种方法 (Map 作用:解决了普通对象key值只能是string 或者 symbol类型的局限)
    • Map 与 数组的相互转化 Array.from(map) 或 [...map]; new Map(arr); // Map 转化为数组为 一个二维数组。
    • map.set(key,value)
    • map.get(key);
    • map.has(key);
    • map.delete(key)
    • 生成一个新的map 数据, new Map(oldMap); 用于更新state
      import { useState } from 'react';

export const useMap = (defaultValue: Array<[a: any, b: any]>): any => {
console.log(new Map(defaultValue));
const [value, setValue] = useState(new Map(defaultValue));

const set = (key: any, value: any) => {
setValue((oldState) => {
oldState.set(key, value);
// 重新生成一个Map数据,保证react的不不可变值
return new Map(Array.from(oldState));
});
};

const get = (key: any) => {
return value.get(key);
};

const remove = (key: any) => {
setValue((oldState) => {
oldState.delete(key);
return new Map(Array.from(oldState));
});
};

const reset = () => {
setValue(new Map(defaultValue));
};

return [value, { set, get, remove, reset }];
};

  1. useSet
  • 作用同useMap,也就是需要使用Set类型的state时候使用。
  • 回顾Set的方法
    • add
    • delete
    • has
    • clear
    • size
      import { useState } from 'react';

export const useSet = (defaultValue): any => {
const [state, setState] = useState(new Set(defaultValue));
const add = (v) => {
setState((oldState) => {
oldState.add(v);
return new Set(oldState);
});
};
const remove = (v) => {
setState((oldState) => {
oldState.delete(v);
return new Set(oldState);
});
};
const reset = () => {
setState(() => {
return new Set(defaultValue);
});
};
return [state, { add, remove, reset }];
};

  1. usePrevious
    作用:记录上一次状态的hooks
    原理:利用useRef 来保存上一次状态的值
    import { useState, useEffect, useRef } from 'react';

export const usePrevious = (value: T, shouldUpdate?: (pre: T, next: T) => boolean) => {
const [state, setState] = useState();
const preValue = useRef(value);
useEffect(() => {
if (shouldUpdate) {
if (shouldUpdate(preValue.current, value)) {
setState(preValue.current);
}
} else {
setState(preValue.current);
}
// 写法简化
preValue.current = value;
}, [value]);
return [state];
};

  1. useRafState
    作用: 只在 requestAnimationFrame callback 时更新 state,一般用于性能优化。
    import { useState, useCallback, useRef, useEffect } from 'react';
    import type { Dispatch, SetStateAction } from 'react';

function useRafState(initialState: T | (() => T)): [T, Dispatch<SetStateAction>];
function useRafState<T = undefined>(): [T | undefined, Dispatch<SetStateAction<T | undefined>>];

function useRafState(value?: T): [state: T, setRafState: (v: T) => void] {
const [state, setState] = useState(value);
const ref = useRef(0);

const setRafState = useCallback((v: T) => {
cancelAnimationFrame(ref.current);
ref.current = requestAnimationFrame(() => {
setState(v);
});
}, []);
useEffect(() => {
cancelAnimationFrame(ref.current);
}, []);

return [state, setRafState];
}
export { useRafState };

  1. useSafeState
    作用:用法与 React.useState 完全一样,但是在组件卸载后异步回调内的 setState 不再执行,避免因组件卸载后更新状态而导致的内存泄漏。
    import { useState, useEffect, useRef } from 'react';
    import type { Dispatch, SetStateAction } from 'react';

function useSafeState(initialState: T | (() => T)): [T, Dispatch<SetStateAction>];
function useSafeState<T = undefined>(): [T | undefined, Dispatch<SetStateAction<T | undefined>>];
function useSafeState(initialState?: T | (() => T)) {
const [state, setState] = useState(initialState);
const isUnMountRef = useRef(false);
useEffect(() => {
return () => {
isUnMountRef.current = true;
};
}, []);
const setValue = (v: T | (() => T)) => {
if (isUnMountRef.current) return;
setState(v);
};
return [state, setValue];
}
export { useSafeState };

  1. useGetState
    作用:给 React.useState 增加了一个 getter 方法,以获取当前最新值。使用在 react 事件系统外的函数中,比如 定时器回调函数,dom监听函数 等 解决 这些函数中不能同步获取最新的state 的问题。
    实现原理:通过ref 保存最新的state的值,在getter函数中返回。
    import { useState, useRef, useCallback } from 'react';
    import type { Dispatch, SetStateAction } from 'react';

function useGetState(initialState: T | (() => T)): [T, Dispatch<SetStateAction>, () => T];
function useGetState<T = undefined>(): [T, Dispatch<SetStateAction<T | undefined>>, () => T | undefined];
function useGetState(initialState?: T | (() => T)) {
const [state, setState] = useState(initialState);
const stateRef = useRef();
stateRef.current = state;

const getState = useCallback(() => {
return stateRef.current;
}, []);
return [state, setState, getState];
}

export { useGetState };

  1. useResetState
    作用: 提供重置 state 方法的 Hooks,用法与 React.useState 基本一致。
    import { useState, useRef, useCallback } from 'react';
    import type { Dispatch, SetStateAction } from 'react';

function useResetState(initialState: T | (() => T)): [T, Dispatch<SetStateAction>, () => T];
function useResetState<T = undefined>(): [T, Dispatch<SetStateAction<T | undefined>>, () => T | undefined];
function useResetState(initialState?: T | (() => T)) {
const [state, setState] = useState(initialState);
const stateRef = useRef(initialState);

const resetState = useCallback(() => {
setState(stateRef.current);
}, []);
return [state, setState, resetState];
}

export { useResetState };

  1. useUrlState
    用法:可以将url中query 和 state 相结合使用。通过操作state 来控制url 中query 数据的展示。
    React-router 中常用路由方法; 页面组件可以直接通过this.props 获取,普通组件也可以通过(withRouter 高阶组件包裹)使用。
  • location 中的search 字段可以获取url 的query 数据。
  • history 中包含了组件常用的方法,常用的包含push 和 replace,两者都是页面跳转,区别是replace 只是改变url 不会引起页面刷新,且不会生成历史记录,无法回退。
  • match:包含了具体的 url 信息,在 params 字段中可以获取到各个路由参数的值。
    import * as tmp from 'react-router';

// ignore waring "export 'useNavigate' (imported as 'rc') was not found in 'react-router'
const rc = tmp as any;

const location = rc.useLocation();

// react-router v5
const history = rc.useHistory?.();
// react-router v6
const navigate = rc.useNavigate?.();

const setState = (s: React.SetStateAction) => {
const newQuery = typeof s === 'function' ? s(targetQuery) : s;

// 1. 如果 setState 后,search 没变化,就需要 update 来触发一次更新。比如 demo1 直接点击 clear,就需要 update 来触发更新。
// 2. update 和 history 的更新会合并,不会造成多次更新
update();
if (history) {
  history[navigateMode](
    {
      hash: location.hash,
      search: stringify({ ...queryFromUrl, ...newQuery }, mergedStringifyOptions) || '?',
    },
    location.state,
  );
}
if (navigate) {
  navigate(
    {
      hash: location.hash,
      search: stringify({ ...queryFromUrl, ...newQuery }, mergedStringifyOptions) || '?',
    },
    {
      replace: navigateMode === 'replace',
      state: location.state,
    },
  );
}

};
LifeCycle

  1. useMount
    作用: 只在组件初始化时执行的 Hook。
    优势:语义化,防止写太多相同的useEffect写不同的功能;
    import { useEffect } from 'react';

function useMount(fn: () => void) {
useEffect(() => {
fn?.();
}, []);
}

export { useMount };

  1. useUnmount
    作用:只在组件卸载时执行的 Hook。
    import { useEffect } from 'react';

function useUnmount(fn: () => void) {
useEffect(() => {
return () => {
fn?.();
};
}, []);
}

export { useUnmount };

  1. useUnmountedRef
    作用:返回一个ref,current 值为boolen类型,代表该组件是否已经被卸载了;
    import { useRef, useEffect } from 'react';

function useUnmountedRef() {
const unMountedRef = useRef(false);
useEffect(() => {
return () => {
unMountedRef.current = true;
};
}, []);
return unMountedRef;
}

export { useUnmountedRef };
Effect 模块

  1. useUpdate
    作用:useUpdate 会返回一个函数,调用该函数会强制组件重新渲染。
    import { useState } from 'react';

function useUpdate() {
const [, setState] = useState(null);
const update = () => {
setState({});
};
return update;
}

export { useUpdate };
2. useUpdateEffect(useUpdateLayoutEffect同理)
作用:忽略第一次useEffect 执行,仅仅在依赖update 的时候进行更新。
import { useRef, useEffect } from 'react';

function useUpdateEffect(fn: () => void, deps: any[] = []) {
const isMountedRef = useRef(false);
useEffect(() => {
isMountedRef.current = true;
}, []);

useEffect(() => {
if (isMountedRef.current) {
fn?.();
}
}, deps);
}

export { useUpdateEffect };

posted on 2023-10-17 08:42  长安城下翩翩少年  阅读(11)  评论(0编辑  收藏  举报