首页 > 编程语言 >ahooks 源码实现

ahooks 源码实现

时间:2023-10-17 09:02:52浏览次数:42  
标签:const 实现 value return state 源码 useState ahooks setState

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 };

标签:const,实现,value,return,state,源码,useState,ahooks,setState
From: https://www.cnblogs.com/honkerzh/p/17768845.html

相关文章

  • docker 搭建nextcloud。实现个人网盘nextcloud
    想要docker启用高可用的Nextcloud网站,我们需要安装一下docker-compose安装docker-composewgethttps://github.com/docker/compose/releases/download/v2.5.0/docker-compose-linux-x86_64-O/usr/local/bin/docker-compose下载完成,添加运行权限chmod+x/usr/local/bin/dock......
  • C语言实现顺序表二
    ////main.c//SeqList2////Createdbystevexiaohuzhaoon2023/10/15.//#include<stdio.h>#include<stdlib.h>#defineMAXSIZE100/*表示线性表的最大长度*///定义一个顺序表节点structSNode{//用来存储书序表中的数据(动态分配数组)......
  • Linux下源码编译gcc指定版本
    首先你得有一个编译器才能编译编译器下载GCC源码并解压wgethttps://mirrors.tuna.tsinghua.edu.cn/gnu/gcc/gcc-9.4.0/gcc-9.4.0.tar.gztar-zxvfgcc-9.4.0.tar.gz这里我选择了gcc-9.4.0版本下载依赖文件cdgcc-9.4.0./contrib/download_prerequisites编译前配置......
  • ReentrantLock怎么实现公平锁的?
    newReentrantLock()构造函数默认创建的是非公平锁NonfairSyncpublicReentrantLock(){ sync=newNonfairSync();}同时也可以在创建锁构造函数中传入具体参数创建公平锁FairSyncReentrantLocklock=newReentrantLock(true);---ReentrantLock//true代表公平锁,fa......
  • java实现大文件多线程上传案例
    当机器内存大小为4G,需要上传一个大小为50G的文件时,为了避免内存溢出,可以采用分片上传的方式,即将大文件切分成多个小片段进行并发上传。以下是一个详细的方案和代码实现示例:方案说明:将大文件切分成多个大小适当的片段(例如每个片段大小为100MB)。创建一个线程池来管理并发上传任务,......
  • js 函数如何实现策略模式与状态模式
    前言有关设计模式的学习资料中,大部分都是以java语言实现的,毕竟java作为老牌面向对象的语言最能说明设计模式的核心概念,所以js的相关设计模式的学习资料也大多使用class类实现,本文记录下js使用函数实现策略模式和状态模式设计模式的方式,更有助于理解策略模式和状态模式......
  • java中接口的实现方式
    目录Java8接口初始化的几种场景通过接口实现类的方式实现1.定义接口2.接口实现3.测试方法通过匿名内部类的来实现接口实现3.测试方法通过方法的引用1.实现通过箭头函数Lambda表达式的方式1.定义接口2.接口实现3.测试方法将接口作为方法参数1.定义一个方法2.调用方法并......
  • 用低代码打造CRM系统 实现客户个性化管理
    CRM管理思想伴随着互联网和电子商务的大潮进入中国,对企业经营管理理念产生了巨大的冲击,使中国企业逐步树立起与世界接轨的客户关系管理理念。中国CRM市场的萌芽阶段大约始于2000年,而到2010年前后,中国市场上争相涌现了一大批CRM软件厂商,国内外的CRM厂商都在为中国企业的数字化转型......
  • Idea调试Tomcat源码
    一、下载Tomcat源码SourceCode:https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.93/src/apache-tomcat-8.5.93-src.zipBinary:https://dlcdn.apache.org/tomcat/tomcat-8/v8.5.93/bin/apache-tomcat-8.5.93.zip建议保持这俩版本一致,不然会出现各种找不到类,方法的情况等。解......
  • 第二十一篇 - vue中实现页面刷新以及局部刷新的方法
    参考链接:https://blog.csdn.net/qq_41117240/article/details/127275478第一步:在需要局部刷新的标签添加 第二步:在data里面初始化isRefresh的值为true 第三步:在method里面添加刷新函数 第四步:在需要局部刷新的地方调用刷新函数this.updateRefresh()......