=> {
30 | // @ts-ignore
31 | return fnRef.current(...args)
32 | },
33 | wait,
34 | options
35 | ),
36 | []
37 | )
38 |
39 | useUnmount(() => {
40 | debounced.cancel()
41 | })
42 |
43 | return {
44 | run: debounced,
45 | cancel: debounced.cancel,
46 | flush: debounced.flush
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/effect/useDeepCompareEffect.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Jaina Xiong
3 | * @Email: 17761608@qq.com
4 | * @Date: 2025-07-09 12:43:07
5 | * @LastEditors: Jaina Xiong
6 | * @LastEditTime: 2025-08-05 19:36:14
7 | */
8 | import { useEffect } from 'react'
9 | import { useDeepCompareMemoize } from '../_utils/useDeepCompareMemoize'
10 |
11 | /**
12 | * @name 依赖项更新时,深度比较
13 | * @description
14 | * 依赖项更新时,对输入进行深度比较,而不是引用相等
15 | * @example
16 | * useDeepCompareEffect(
17 | () => {
18 | // make an HTTP request or whatever with the query and variables
19 | // optionally return a cleanup function if necessary
20 | },[query, variables],)
21 | */
22 | export const useDeepCompareEffect = (effect: any, dependencies: any) => {
23 | useEffect(effect, useDeepCompareMemoize(dependencies))
24 | }
25 |
--------------------------------------------------------------------------------
/src/effect/useInterval.ts:
--------------------------------------------------------------------------------
1 | //@ts-nocheck
2 |
3 | import { useCallback, useEffect, useRef } from "react"
4 | import { useLatest } from "common-hook"
5 | import { isNumber } from "common-screw"
6 |
7 | /**
8 | * @name 处理setInterval的Hook
9 | * @description 例如:每1000ms,执行一次
10 | * @example
11 | * useInterval(() => {
12 | setCount(count + 1);
13 | }, 1000);
14 | */
15 | export const useInterval = (
16 | fn: () => void,
17 | delay: number | undefined,
18 | options?: {
19 | immediate?: boolean
20 | }
21 | ) => {
22 | const immediate = options?.immediate
23 |
24 | const fnRef = useLatest(fn)
25 | const timerRef = useRef()
26 |
27 | useEffect(() => {
28 | //@ts-ignore
29 | if (!isNumber(delay) || delay < 0) return
30 |
31 | if (immediate) {
32 | fnRef.current()
33 | }
34 | timerRef.current = setInterval(() => {
35 | fnRef.current()
36 | }, delay)
37 | return () => {
38 | if (timerRef.current) {
39 | clearInterval(timerRef.current as NodeJS.Timer)
40 | }
41 | }
42 | }, [delay])
43 |
44 | const clear = useCallback(() => {
45 | if (timerRef.current) {
46 | clearInterval(timerRef.current as NodeJS.Timer)
47 | }
48 | }, [])
49 |
50 | return clear
51 | }
52 |
--------------------------------------------------------------------------------
/src/effect/useLockFn.ts:
--------------------------------------------------------------------------------
1 | import { useRef, useCallback } from "react"
2 |
3 | /**
4 | * @name 给一个异步函数增加竞态锁,防止并发执行
5 | * @description 在 submit 函数执行完成前,其余的点击动作都会被忽略。
6 | * @example
7 | * const submit = useLockFn(async () => {
8 | await mockApiRequest();
9 | setCount((val) => val + 1);
10 | });
11 | */
12 | export const useLockFn = (
13 | fn: (...args: P) => Promise
14 | ) => {
15 | const lockRef = useRef(false)
16 |
17 | return useCallback(
18 | async (...args: P) => {
19 | if (lockRef.current) return
20 | lockRef.current = true
21 | try {
22 | const ret = await fn(...args)
23 | lockRef.current = false
24 | return ret
25 | } catch (e) {
26 | lockRef.current = false
27 | throw e
28 | }
29 | },
30 | [fn]
31 | )
32 | }
33 |
--------------------------------------------------------------------------------
/src/effect/useThrottleEffect.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react"
2 | import { useThrottleFn, useUpdateEffect } from "common-hook"
3 |
4 | /**
5 | * @name useEffect+节流
6 | * @description
7 | * 规定在时间内,只能触发一次函数。如果这个时间内触发多次函数,只有一次生效
8 | * @example
9 | * useThrottleEffect(
10 | () => {
11 | setRecords((val) => [...val, value])
12 | },
13 | [value],
14 | {
15 | wait: 1000
16 | }
17 | )
18 | */
19 | export const useThrottleEffect = (effect: any, deps?: any, options?: any) => {
20 | const [flag, setFlag] = useState({})
21 |
22 | const { run } = useThrottleFn(() => {
23 | setFlag({})
24 | }, options)
25 |
26 | useEffect(() => {
27 | return run()
28 | }, deps)
29 |
30 | useUpdateEffect(effect, [flag])
31 | }
32 |
--------------------------------------------------------------------------------
/src/effect/useThrottleFn.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react"
2 | import { useUnmount, useLatest } from "common-hook"
3 | import { throttle } from "common-screw"
4 |
5 | type noop = (...args: any) => any
6 |
7 | /**
8 | * @name 处理函数节流的Hook
9 | * @description
10 | * 规定在时间内,只能触发一次函数。如果这个时间内触发多次函数,只有一次生效
11 | * @example
12 | * const { run } = useThrottleFn(
13 | () => {
14 | setValue(value + 1);
15 | },
16 | { wait: 500 },
17 | );
18 | */
19 | export const useThrottleFn = (fn: T, options?: any) => {
20 | const fnRef = useLatest(fn)
21 |
22 | const wait = options?.wait ?? 1000
23 |
24 | const throttled = useMemo(
25 | () =>
26 | throttle(
27 | (...args: Parameters): ReturnType => {
28 | // @ts-ignore
29 | return fnRef.current(...args)
30 | },
31 | wait,
32 | options
33 | ),
34 | []
35 | )
36 |
37 | useUnmount(() => {
38 | throttled.cancel()
39 | })
40 |
41 | return {
42 | run: throttled,
43 | cancel: throttled.cancel,
44 | flush: throttled.flush
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/effect/useTimeout.ts:
--------------------------------------------------------------------------------
1 | //@ts-nocheck
2 | import { useCallback, useEffect, useRef } from "react"
3 | import { useLatest } from "common-hook"
4 | import { isNumber } from "common-screw"
5 |
6 | /**
7 | * @name 处理setTimeout的Hook
8 | * @description 例如:3000ms 后执行一次
9 | * @example
10 | * useTimeout(() => {setState(state + 1)}, 3000)
11 | */
12 | export const useTimeout = (fn: () => void, delay: number | undefined) => {
13 | const fnRef = useLatest(fn)
14 | const timerRef = useRef()
15 |
16 | useEffect(() => {
17 | //@ts-ignore
18 | if (!isNumber(delay) || delay < 0) return
19 |
20 | timerRef.current = setTimeout(() => {
21 | fnRef.current()
22 | }, delay)
23 | return () => {
24 | if (timerRef.current) {
25 | clearTimeout(timerRef.current as NodeJS.Timer)
26 | }
27 | }
28 | }, [delay])
29 |
30 | const clear = useCallback(() => {
31 | if (timerRef.current) {
32 | clearTimeout(timerRef.current as NodeJS.Timer)
33 | }
34 | }, [])
35 |
36 | return clear
37 | }
38 |
--------------------------------------------------------------------------------
/src/effect/useUpdateEffect.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useRef } from "react"
2 | import type { useEffect as useEffectType, useLayoutEffect } from "react"
3 |
4 | type EffectHookType = typeof useEffectType | typeof useLayoutEffect
5 |
6 | const createUpdateEffect: (hook: EffectHookType) => EffectHookType =
7 | (hook) => (effect: any, deps: any) => {
8 | const isMounted = useRef(false)
9 | hook(() => {
10 | return () => {
11 | isMounted.current = false
12 | }
13 | }, [])
14 |
15 | hook(() => {
16 | if (!isMounted.current) {
17 | isMounted.current = true
18 | } else {
19 | return effect()
20 | }
21 | }, deps)
22 | }
23 |
24 | /**
25 | * @name 首次不执行,只在依赖项更新时执行
26 | * @description 使用上与useEffect完全相同,只是它忽略了首次执行,只在依赖项更新时执行
27 | * @example
28 | * useUpdateEffect(() => {
29 | setUpdateEffectCount((c) => c + 1);
30 | }, [count]);
31 | */
32 | export const useUpdateEffect = createUpdateEffect(useEffect)
33 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./advanced" // 辅助 Hooks
2 | export * from "./dom" // 浏览器 Hooks
3 | export * from "./effect" // Effect Hooks
4 | export * from "./lifeCycle" // 生命周期 Hooks
5 | export * from "./state" // 状态 Hooks
6 |
--------------------------------------------------------------------------------
/src/lifeCycle/index.ts:
--------------------------------------------------------------------------------
1 | import { useMount } from "./useMount"
2 | import { useUnmount } from "./useUnmount"
3 |
4 | export * from "./useMount"
5 | export * from "./useUnmount"
6 |
7 | /**
8 | * @name 生命周期 Hooks
9 | * @example
10 | * useMount // 在组件首次渲染时,执行方法
11 | * useUnmount // 在组件卸载时,执行函数
12 | */
13 | export const LifeCycle = {
14 | useMount,
15 | useUnmount
16 | }
17 |
--------------------------------------------------------------------------------
/src/lifeCycle/useMount.ts:
--------------------------------------------------------------------------------
1 | //@ts-ignore
2 | import { useEffect } from "react"
3 |
4 | /**
5 | * @name 在组件首次渲染时,执行方法
6 | * @description 只在组件初始化时执行的 Hook
7 | * @example
8 | * useMount(fn)
9 | */
10 | export const useMount = (fn: () => void) => {
11 | useEffect(() => {
12 | fn?.()
13 | }, [])
14 | }
15 |
--------------------------------------------------------------------------------
/src/lifeCycle/useUnmount.ts:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react"
2 | import { useLatest } from "common-hook"
3 |
4 | /**
5 | * @name 在组件卸载时,执行函数
6 | * @description 在组件卸载(unmount)时执行的 Hook
7 | * @example
8 | * useUnmount(fn)
9 | */
10 | export const useUnmount = (fn: () => void) => {
11 | const fnRef = useLatest(fn)
12 |
13 | useEffect(
14 | () => () => {
15 | fnRef.current()
16 | },
17 | []
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/src/state/index.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Jaina Xiong
3 | * @Email: 17761608@qq.com
4 | * @Date: 2025-07-09 12:43:07
5 | * @LastEditors: Jaina Xiong
6 | * @LastEditTime: 2025-08-13 20:31:14
7 | */
8 | import { useBoolean } from './useBoolean'
9 | import { useDebounce } from './useDebounce'
10 | import { useRafState } from './useRafState'
11 | import { useSetState } from './useSetState'
12 | import { useThrottle } from './useThrottle'
13 | import { useToggle } from './useToggle'
14 |
15 | export * from './useBoolean'
16 | export * from './useDebounce'
17 | export * from './useRafState'
18 | export * from './useSetState'
19 | export * from './useThrottle'
20 | export * from './useToggle'
21 |
22 | /**
23 | * @name 状态 Hooks
24 | * @example
25 | * useSetState // 管理 object 类型 state 的 Hooks
26 | * useBoolean // 切换boolean,可以接收默认值
27 | * useToggle // 用于在两个状态值间切换Hook
28 | * useDebounce // 处理防抖值Hook
29 | * useThrottle // 处理节流值Hook
30 | * useRafState // 只在 requestAnimationFrame callback 时更新 state
31 | */
32 | export const State = {
33 | useBoolean,
34 | useDebounce,
35 | useRafState,
36 | useSetState,
37 | useThrottle,
38 | useToggle
39 | }
40 |
41 | //写一个hook的计数器 有加减的分别按钮的点击事件,不要ts
42 |
--------------------------------------------------------------------------------
/src/state/useBoolean.ts:
--------------------------------------------------------------------------------
1 | import { useMemo } from "react"
2 | import { useToggle } from "common-hook"
3 |
4 | /**
5 | * @name 切换boolean,可以接收默认值
6 | * @example
7 | * const [state, { toggle, setTrue, setFalse }] = useBoolean(true)
8 | */
9 | export const useBoolean = (defaultValue = false) => {
10 | const [state, { toggle, set }] = useToggle(defaultValue)
11 |
12 | const actions = useMemo(() => {
13 | const setTrue = () => set(true)
14 | const setFalse = () => set(false)
15 | return {
16 | toggle,
17 | set: (v: any) => set(!!v),
18 | setTrue,
19 | setFalse
20 | }
21 | }, [])
22 |
23 | return [state, actions]
24 | }
25 |
--------------------------------------------------------------------------------
/src/state/useDebounce.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react"
2 | import { useDebounceFn } from "common-hook"
3 |
4 | /**
5 | * @name 处理防抖值Hook
6 | * @description
7 | * 在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时
8 | * @example
9 | * const debouncedValue = useDebounce(value, { wait: 500 })
10 | */
11 | export const useDebounce = (value: any, options?: any) => {
12 | const [debounced, setDebounced] = useState(value)
13 |
14 | const { run } = useDebounceFn(() => {
15 | setDebounced(value)
16 | }, options)
17 |
18 | useEffect(() => {
19 | run()
20 | }, [value])
21 |
22 | return debounced
23 | }
24 |
--------------------------------------------------------------------------------
/src/state/useRafState.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useRef, useState } from "react"
2 | import type { Dispatch, SetStateAction } from "react"
3 | import { useUnmount } from "common-hook"
4 |
5 | /**
6 | * @name 只在 requestAnimationFrame callback 时更新 state
7 | * @description 一般用于性能优化
8 | * @example
9 | * const [state, setState] = useRafState({ width: 0,height: 0})
10 | */
11 | export const useRafState = (initialState?: S | (() => S)) => {
12 | const ref = useRef(0)
13 | const [state, setState] = useState(initialState)
14 |
15 | const setRafState = useCallback((value: S | ((prevState: S) => S)) => {
16 | cancelAnimationFrame(ref.current)
17 |
18 | ref.current = requestAnimationFrame(() => {
19 | setState(value)
20 | })
21 | }, [])
22 |
23 | useUnmount(() => {
24 | cancelAnimationFrame(ref.current)
25 | })
26 |
27 | return [state, setRafState] as const
28 | }
29 |
--------------------------------------------------------------------------------
/src/state/useSetState.ts:
--------------------------------------------------------------------------------
1 | import { useCallback, useState } from "react"
2 | import { isFunction } from "common-screw"
3 |
4 | type SetState> = (
5 | state: Pick | null | ((prevState: Readonly) => Pick | S | null)
6 | ) => void
7 |
8 | /**
9 | * @name 管理 object 类型 state 的 Hooks
10 | * @description 用法与 class 组件的 this.setState 基本一致
11 | * @example
12 | * const [state, setState] = useSetState({hello: '',count: 0})
13 | */
14 | export const useSetState = >(
15 | initialState: S | (() => S)
16 | ): [S, SetState] => {
17 | const [state, setState] = useState(initialState)
18 |
19 | const setMergeState = useCallback((patch: any) => {
20 | setState((prevState: any) => {
21 | const newState = isFunction(patch) ? patch(prevState) : patch
22 | return newState ? { ...prevState, ...newState } : prevState
23 | })
24 | }, [])
25 |
26 | return [state, setMergeState]
27 | }
28 |
--------------------------------------------------------------------------------
/src/state/useThrottle.ts:
--------------------------------------------------------------------------------
1 | import { useEffect, useState } from "react"
2 | import { useThrottleFn } from "common-hook"
3 |
4 | /**
5 | * @name 处理节流值Hook
6 | * @description
7 | * 规定在时间内,只能触发一次函数。如果这个时间内触发多次函数,只有一次生效
8 | * @example
9 | * const throttledValue = useThrottle(value, { wait: 500 })
10 | */
11 | export const useThrottle = (value: T, options?: any) => {
12 | const [throttled, setThrottled] = useState(value)
13 |
14 | const { run } = useThrottleFn(() => {
15 | setThrottled(value)
16 | }, options)
17 |
18 | useEffect(() => {
19 | run()
20 | }, [value])
21 |
22 | return throttled
23 | }
24 |
--------------------------------------------------------------------------------
/src/state/useToggle.ts:
--------------------------------------------------------------------------------
1 | import { useMemo, useState } from "react"
2 |
3 | /**
4 | * @name 用于在两个状态值间切换Hook
5 | * @description 默认为 boolean 切换,基础用法与 useBoolean 一致
6 | * @example
7 | * const [state, { toggle, setLeft, setRight }] = useToggle()
8 | */
9 | export const useToggle = (
10 | defaultValue: D = false as unknown as D,
11 | reverseValue?: R
12 | ) => {
13 | const [state, setState] = useState(defaultValue)
14 |
15 | const actions = useMemo(() => {
16 | const reverseValueOrigin = (
17 | reverseValue === undefined ? !defaultValue : reverseValue
18 | ) as D | R
19 |
20 | const toggle = () =>
21 | setState((s: any) =>
22 | s === defaultValue ? reverseValueOrigin : defaultValue
23 | )
24 | const set = (value: D | R) => setState(value)
25 | const setLeft = () => setState(defaultValue)
26 | const setRight = () => setState(reverseValueOrigin)
27 |
28 | return {
29 | toggle,
30 | set,
31 | setLeft,
32 | setRight
33 | }
34 | }, [])
35 |
36 | return [state, actions]
37 | }
38 |
--------------------------------------------------------------------------------
/src/typings/index.d.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: Jaina Xiong
3 | * @Email: 17761608@qq.com
4 | * @Date: 2025-07-09 12:43:07
5 | * @LastEditors: Jaina Xiong
6 | * @LastEditTime: 2025-08-05 19:52:27
7 | */
8 | declare module 'react'
9 | declare module 'common-hook'
10 | declare module 'common-screw'
11 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig.json to read more about this file */
4 |
5 | /* Basic Options */
6 | // "incremental": true, /* Enable incremental compilation */
7 | "target": "es6" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
8 | "module": "es2015" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
9 | // "lib": [], /* Specify library files to be included in the compilation. */
10 | // "allowJs": true, /* Allow javascript files to be compiled. */
11 | // "checkJs": true, /* Report errors in .js files. */
12 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
13 | "declaration": true /* Generates corresponding '.d.ts' file. */,
14 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
15 | // "sourceMap": true, /* Generates corresponding '.map' file. */
16 | // "outFile": "./", /* Concatenate and emit output to single file. */
17 | "outDir": "./es" /* Redirect output structure to the directory. */,
18 | // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
19 | // "composite": true, /* Enable project compilation */
20 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
21 | // "removeComments": true, /* Do not emit comments to output. */
22 | // "noEmit": true, /* Do not emit outputs. */
23 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
24 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
25 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
26 | /* Strict Type-Checking Options */
27 | "strict": true /* Enable all strict type-checking options. */,
28 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
29 | // "strictNullChecks": true, /* Enable strict null checks. */
30 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
31 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
32 | // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
33 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
34 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
35 |
36 | /* Additional Checks */
37 | // "noUnusedLocals": true, /* Report errors on unused locals. */
38 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
39 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
40 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
41 | // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
42 |
43 | /* Module Resolution Options */
44 | "moduleResolution": "node" /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */,
45 | // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
46 | // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
47 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
48 | // "typeRoots": [], /* List of folders to include type definitions from. */
49 | // "types": [], /* Type declaration files to be included in compilation. */
50 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
51 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
52 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
53 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
54 |
55 | /* Source Map Options */
56 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
57 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
58 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
59 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
60 |
61 | /* Experimental Options */
62 | // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
63 | // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
64 |
65 | /* Advanced Options */
66 | "skipLibCheck": true /* Skip type checking of declaration files. */,
67 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
68 | },
69 | "include": ["./src"],
70 | "exclude": ["node_modules", "es", "**/test", "src/_testComponent"]
71 | }
72 |
--------------------------------------------------------------------------------