react-simplikit 0.0.48 → 0.0.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ImpressionArea/index.cjs +7 -4
- package/dist/components/Separated/index.cjs +1 -1
- package/dist/hooks/useCallbackOncePerRender/index.cjs +6 -3
- package/dist/hooks/useDebounce/index.cjs +14 -8
- package/dist/hooks/useDebouncedCallback/index.cjs +7 -4
- package/dist/hooks/useDoubleClick/index.cjs +6 -3
- package/dist/hooks/useImpressionRef/index.cjs +7 -4
- package/dist/hooks/useIntersectionObserver/index.cjs +6 -3
- package/dist/hooks/useInterval/index.cjs +24 -15
- package/dist/hooks/useIsClient/index.cjs +40 -0
- package/dist/hooks/useIsClient/index.d.cts +40 -0
- package/dist/hooks/useList/index.cjs +104 -0
- package/dist/hooks/useList/index.d.cts +50 -0
- package/dist/hooks/useLoading/index.cjs +9 -6
- package/dist/hooks/useLongPress/index.cjs +6 -3
- package/dist/hooks/useOutsideClickEffect/index.cjs +6 -3
- package/dist/hooks/usePreservedCallback/index.cjs +6 -3
- package/dist/hooks/useRefEffect/index.cjs +6 -3
- package/dist/hooks/useRefEffect/index.d.cts +3 -3
- package/dist/hooks/useSet/index.cjs +105 -0
- package/dist/hooks/useSet/index.d.cts +39 -0
- package/dist/hooks/useThrottle/index.cjs +6 -3
- package/dist/hooks/useThrottledCallback/index.cjs +172 -0
- package/dist/hooks/useThrottledCallback/index.d.cts +30 -0
- package/dist/hooks/useTimeout/index.cjs +8 -5
- package/dist/hooks/useTimeout/index.d.cts +1 -1
- package/dist/index.cjs +252 -96
- package/dist/index.d.cts +4 -0
- package/esm/components/ImpressionArea/index.js +7 -4
- package/esm/components/Separated/index.js +1 -1
- package/esm/hooks/useCallbackOncePerRender/index.js +6 -3
- package/esm/hooks/useDebounce/index.js +14 -8
- package/esm/hooks/useDebouncedCallback/index.js +7 -4
- package/esm/hooks/useDoubleClick/index.js +6 -3
- package/esm/hooks/useImpressionRef/index.js +7 -4
- package/esm/hooks/useIntersectionObserver/index.js +6 -3
- package/esm/hooks/useInterval/index.js +24 -15
- package/esm/hooks/useIsClient/index.d.ts +40 -0
- package/esm/hooks/useIsClient/index.js +14 -0
- package/esm/hooks/useList/index.d.ts +50 -0
- package/esm/hooks/useList/index.js +78 -0
- package/esm/hooks/useLoading/index.js +9 -6
- package/esm/hooks/useLongPress/index.js +6 -3
- package/esm/hooks/useOutsideClickEffect/index.js +6 -3
- package/esm/hooks/usePreservedCallback/index.js +6 -3
- package/esm/hooks/useRefEffect/index.d.ts +3 -3
- package/esm/hooks/useRefEffect/index.js +6 -3
- package/esm/hooks/useSet/index.d.ts +39 -0
- package/esm/hooks/useSet/index.js +79 -0
- package/esm/hooks/useThrottle/index.js +6 -3
- package/esm/hooks/useThrottledCallback/index.d.ts +30 -0
- package/esm/hooks/useThrottledCallback/index.js +146 -0
- package/esm/hooks/useTimeout/index.d.ts +1 -1
- package/esm/hooks/useTimeout/index.js +8 -5
- package/esm/index.d.ts +4 -0
- package/esm/index.js +222 -70
- package/package.json +1 -1
|
@@ -8,9 +8,12 @@ import { useMemo } from "react";
|
|
|
8
8
|
import { useCallback, useEffect, useRef } from "react";
|
|
9
9
|
function usePreservedCallback(callback) {
|
|
10
10
|
const callbackRef = useRef(callback);
|
|
11
|
-
useEffect(
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
useEffect(
|
|
12
|
+
function syncCallbackRef() {
|
|
13
|
+
callbackRef.current = callback;
|
|
14
|
+
},
|
|
15
|
+
[callback]
|
|
16
|
+
);
|
|
14
17
|
return useCallback((...args) => {
|
|
15
18
|
return callbackRef.current(...args);
|
|
16
19
|
}, []);
|
|
@@ -86,11 +89,14 @@ function useDebounce(callback, wait, options = {}) {
|
|
|
86
89
|
const debounced = useMemo(() => {
|
|
87
90
|
return debounce(preservedCallback, wait, { edges });
|
|
88
91
|
}, [preservedCallback, wait, edges]);
|
|
89
|
-
useEffect2(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
92
|
+
useEffect2(
|
|
93
|
+
function cancelDebouncedOnUnmount() {
|
|
94
|
+
return () => {
|
|
95
|
+
debounced.cancel();
|
|
96
|
+
};
|
|
97
|
+
},
|
|
98
|
+
[debounced]
|
|
99
|
+
);
|
|
94
100
|
return debounced;
|
|
95
101
|
}
|
|
96
102
|
export {
|
|
@@ -60,9 +60,12 @@ function debounce(func, debounceMs, { edges = ["leading", "trailing"] } = {}) {
|
|
|
60
60
|
import { useCallback, useEffect, useRef } from "react";
|
|
61
61
|
function usePreservedCallback(callback) {
|
|
62
62
|
const callbackRef = useRef(callback);
|
|
63
|
-
useEffect(
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
useEffect(
|
|
64
|
+
function syncCallbackRef() {
|
|
65
|
+
callbackRef.current = callback;
|
|
66
|
+
},
|
|
67
|
+
[callback]
|
|
68
|
+
);
|
|
66
69
|
return useCallback((...args) => {
|
|
67
70
|
return callbackRef.current(...args);
|
|
68
71
|
}, []);
|
|
@@ -78,7 +81,7 @@ function useDebouncedCallback({
|
|
|
78
81
|
const handleChange = usePreservedCallback(onChange);
|
|
79
82
|
const ref = useRef2({ value: false, clearPreviousDebounce: () => {
|
|
80
83
|
} });
|
|
81
|
-
useEffect2(()
|
|
84
|
+
useEffect2(function clearDebouncedOnUnmount() {
|
|
82
85
|
const current = ref.current;
|
|
83
86
|
return () => {
|
|
84
87
|
current.clearPreviousDebounce();
|
|
@@ -7,9 +7,12 @@ import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -63,9 +63,12 @@ function debounce(func, debounceMs, { edges = ["leading", "trailing"] } = {}) {
|
|
|
63
63
|
import { useCallback, useEffect, useRef } from "react";
|
|
64
64
|
function usePreservedCallback(callback) {
|
|
65
65
|
const callbackRef = useRef(callback);
|
|
66
|
-
useEffect(
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
useEffect(
|
|
67
|
+
function syncCallbackRef() {
|
|
68
|
+
callbackRef.current = callback;
|
|
69
|
+
},
|
|
70
|
+
[callback]
|
|
71
|
+
);
|
|
69
72
|
return useCallback((...args) => {
|
|
70
73
|
return callbackRef.current(...args);
|
|
71
74
|
}, []);
|
|
@@ -81,7 +84,7 @@ function useDebouncedCallback({
|
|
|
81
84
|
const handleChange = usePreservedCallback(onChange);
|
|
82
85
|
const ref = useRef2({ value: false, clearPreviousDebounce: () => {
|
|
83
86
|
} });
|
|
84
|
-
useEffect2(()
|
|
87
|
+
useEffect2(function clearDebouncedOnUnmount() {
|
|
85
88
|
const current = ref.current;
|
|
86
89
|
return () => {
|
|
87
90
|
current.clearPreviousDebounce();
|
|
@@ -7,9 +7,12 @@ import { useMemo } from "react";
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -7,9 +7,12 @@ import { useEffect as useEffect2 } from "react";
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -21,18 +24,24 @@ function useInterval(callback, options) {
|
|
|
21
24
|
const immediate = typeof options === "number" ? false : options.immediate;
|
|
22
25
|
const enabled = typeof options === "number" ? true : options.enabled ?? true;
|
|
23
26
|
const preservedCallback = usePreservedCallback(callback);
|
|
24
|
-
useEffect2(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
useEffect2(
|
|
28
|
+
function runImmediateCallback() {
|
|
29
|
+
if (immediate === true && enabled) {
|
|
30
|
+
preservedCallback();
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
[immediate, preservedCallback, enabled]
|
|
34
|
+
);
|
|
35
|
+
useEffect2(
|
|
36
|
+
function startInterval() {
|
|
37
|
+
if (!enabled) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
const id = setInterval(preservedCallback, delay);
|
|
41
|
+
return () => clearInterval(id);
|
|
42
|
+
},
|
|
43
|
+
[delay, preservedCallback, enabled]
|
|
44
|
+
);
|
|
36
45
|
}
|
|
37
46
|
export {
|
|
38
47
|
useInterval
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description
|
|
3
|
+
* `useIsClient` is a React hook that returns `true` only in the client-side environment.
|
|
4
|
+
* It is primarily used to differentiate between client-side and server-side rendering (SSR).
|
|
5
|
+
* The state is set to `true` only after the component is mounted in the client-side environment.
|
|
6
|
+
*
|
|
7
|
+
* @returns {boolean} Returns `true` in a client-side environment, and `false` otherwise.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* function ClientSideContent() {
|
|
11
|
+
* const isClient = useIsClient();
|
|
12
|
+
*
|
|
13
|
+
* if (!isClient) {
|
|
14
|
+
* return <div>Loading...</div>; // Rendered on the server side
|
|
15
|
+
* }
|
|
16
|
+
*
|
|
17
|
+
* return <div>Client-side rendered content</div>; // Rendered on the client side
|
|
18
|
+
* }
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* function ClientOnlyMap() {
|
|
22
|
+
* const isClient = useIsClient();
|
|
23
|
+
*
|
|
24
|
+
* if (!isClient) return null;
|
|
25
|
+
*
|
|
26
|
+
* return <div id="map" />;
|
|
27
|
+
* }
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* function ClientTheme() {
|
|
31
|
+
* const isClient = useIsClient();
|
|
32
|
+
*
|
|
33
|
+
* const theme = isClient ? localStorage.getItem('theme') : 'light';
|
|
34
|
+
*
|
|
35
|
+
* return <div>Current theme: {theme}</div>;
|
|
36
|
+
* }
|
|
37
|
+
*/
|
|
38
|
+
declare function useIsClient(): boolean;
|
|
39
|
+
|
|
40
|
+
export { useIsClient };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/useIsClient/useIsClient.ts
|
|
4
|
+
import { useEffect, useState } from "react";
|
|
5
|
+
function useIsClient() {
|
|
6
|
+
const [isClient, setIsClient] = useState(false);
|
|
7
|
+
useEffect(function syncClientState() {
|
|
8
|
+
setIsClient(true);
|
|
9
|
+
}, []);
|
|
10
|
+
return isClient;
|
|
11
|
+
}
|
|
12
|
+
export {
|
|
13
|
+
useIsClient
|
|
14
|
+
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
type ListActions<T> = {
|
|
2
|
+
push: (value: T) => void;
|
|
3
|
+
insertAt: (index: number, value: T) => void;
|
|
4
|
+
updateAt: (index: number, value: T) => void;
|
|
5
|
+
removeAt: (index: number) => void;
|
|
6
|
+
setAll: (values: T[]) => void;
|
|
7
|
+
reset: () => void;
|
|
8
|
+
};
|
|
9
|
+
type UseListReturn<T> = [ReadonlyArray<T>, ListActions<T>];
|
|
10
|
+
/**
|
|
11
|
+
* @description
|
|
12
|
+
* A React hook that manages an array as state.
|
|
13
|
+
* Provides efficient state management and stable action functions.
|
|
14
|
+
*
|
|
15
|
+
* @param {T[]} initialState - Initial array state
|
|
16
|
+
*
|
|
17
|
+
* @returns {UseListReturn<T>} A tuple containing the array state and actions to manipulate it
|
|
18
|
+
* - `push` - Appends a value to the end of the list
|
|
19
|
+
* - `insertAt` - Inserts a value at the specified index
|
|
20
|
+
* - `updateAt` - Updates the value at the specified index
|
|
21
|
+
* - `removeAt` - Removes the value at the specified index
|
|
22
|
+
* - `setAll` - Replaces the entire list with a new array
|
|
23
|
+
* - `reset` - Resets the list to its initial state
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```tsx
|
|
27
|
+
* const [list, actions] = useList<string>(['apple', 'banana']);
|
|
28
|
+
*
|
|
29
|
+
* // Add an item
|
|
30
|
+
* actions.push('cherry');
|
|
31
|
+
*
|
|
32
|
+
* // Insert at index
|
|
33
|
+
* actions.insertAt(1, 'grape');
|
|
34
|
+
*
|
|
35
|
+
* // Update at index
|
|
36
|
+
* actions.updateAt(0, 'orange');
|
|
37
|
+
*
|
|
38
|
+
* // Remove at index
|
|
39
|
+
* actions.removeAt(2);
|
|
40
|
+
*
|
|
41
|
+
* // Replace all
|
|
42
|
+
* actions.setAll(['kiwi', 'mango']);
|
|
43
|
+
*
|
|
44
|
+
* // Reset to initial state
|
|
45
|
+
* actions.reset();
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
declare function useList<T>(initialState?: T[]): UseListReturn<T>;
|
|
49
|
+
|
|
50
|
+
export { useList };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/useList/useList.ts
|
|
4
|
+
import { useMemo as useMemo2, useState } from "react";
|
|
5
|
+
|
|
6
|
+
// src/hooks/usePreservedCallback/usePreservedCallback.ts
|
|
7
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
8
|
+
function usePreservedCallback(callback) {
|
|
9
|
+
const callbackRef = useRef(callback);
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
16
|
+
return useCallback((...args) => {
|
|
17
|
+
return callbackRef.current(...args);
|
|
18
|
+
}, []);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/hooks/usePreservedReference/usePreservedReference.ts
|
|
22
|
+
import { useMemo, useRef as useRef2 } from "react";
|
|
23
|
+
function usePreservedReference(value, areValuesEqual = areDeeplyEqual) {
|
|
24
|
+
const ref = useRef2(value);
|
|
25
|
+
return useMemo(() => {
|
|
26
|
+
if (!areValuesEqual(ref.current, value)) {
|
|
27
|
+
ref.current = value;
|
|
28
|
+
}
|
|
29
|
+
return ref.current;
|
|
30
|
+
}, [areValuesEqual, value]);
|
|
31
|
+
}
|
|
32
|
+
function areDeeplyEqual(x, y) {
|
|
33
|
+
return JSON.stringify(x) === JSON.stringify(y);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/hooks/useList/useList.ts
|
|
37
|
+
function useList(initialState = []) {
|
|
38
|
+
const [list, setList] = useState(initialState);
|
|
39
|
+
const preservedInitialState = usePreservedReference(initialState);
|
|
40
|
+
const push = usePreservedCallback((value) => {
|
|
41
|
+
setList((prev) => [...prev, value]);
|
|
42
|
+
});
|
|
43
|
+
const insertAt = usePreservedCallback((index, value) => {
|
|
44
|
+
setList((prev) => {
|
|
45
|
+
const next = [...prev];
|
|
46
|
+
next.splice(index, 0, value);
|
|
47
|
+
return next;
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
const updateAt = usePreservedCallback((index, value) => {
|
|
51
|
+
setList((prev) => {
|
|
52
|
+
const next = [...prev];
|
|
53
|
+
next[index] = value;
|
|
54
|
+
return next;
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
const removeAt = usePreservedCallback((index) => {
|
|
58
|
+
setList((prev) => {
|
|
59
|
+
const next = [...prev];
|
|
60
|
+
next.splice(index, 1);
|
|
61
|
+
return next;
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
const setAll = usePreservedCallback((values) => {
|
|
65
|
+
setList(values);
|
|
66
|
+
});
|
|
67
|
+
const reset = usePreservedCallback(() => {
|
|
68
|
+
setList(preservedInitialState);
|
|
69
|
+
});
|
|
70
|
+
const actions = useMemo2(
|
|
71
|
+
() => ({ push, insertAt, updateAt, removeAt, setAll, reset }),
|
|
72
|
+
[push, insertAt, updateAt, removeAt, setAll, reset]
|
|
73
|
+
);
|
|
74
|
+
return [list, actions];
|
|
75
|
+
}
|
|
76
|
+
export {
|
|
77
|
+
useList
|
|
78
|
+
};
|
|
@@ -23,12 +23,15 @@ function useLoading() {
|
|
|
23
23
|
}
|
|
24
24
|
function useIsMountedRef() {
|
|
25
25
|
const ref = useRef({ isMounted: true }).current;
|
|
26
|
-
useEffect(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
useEffect(
|
|
27
|
+
function trackMountedState() {
|
|
28
|
+
ref.isMounted = true;
|
|
29
|
+
return () => {
|
|
30
|
+
ref.isMounted = false;
|
|
31
|
+
};
|
|
32
|
+
},
|
|
33
|
+
[ref]
|
|
34
|
+
);
|
|
32
35
|
return ref;
|
|
33
36
|
}
|
|
34
37
|
export {
|
|
@@ -7,9 +7,12 @@ import { useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -7,9 +7,12 @@ import { useEffect as useEffect2, useRef as useRef2 } from "react";
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
import { useCallback, useEffect, useRef } from "react";
|
|
5
5
|
function usePreservedCallback(callback) {
|
|
6
6
|
const callbackRef = useRef(callback);
|
|
7
|
-
useEffect(
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
useEffect(
|
|
8
|
+
function syncCallbackRef() {
|
|
9
|
+
callbackRef.current = callback;
|
|
10
|
+
},
|
|
11
|
+
[callback]
|
|
12
|
+
);
|
|
10
13
|
return useCallback((...args) => {
|
|
11
14
|
return callbackRef.current(...args);
|
|
12
15
|
}, []);
|
|
@@ -6,10 +6,10 @@ type CleanupCallback = () => void;
|
|
|
6
6
|
* `useRefEffect` is a React hook that helps you set a reference to a specific DOM element and execute a callback whenever the element changes.
|
|
7
7
|
* This hook calls a cleanup function whenever the element changes to prevent memory leaks.
|
|
8
8
|
*
|
|
9
|
-
* @param {(element:
|
|
9
|
+
* @param {(element: RefElement) => CleanupCallback | void} callback - A callback function that is executed when the element is set. This function can return a cleanup function.
|
|
10
10
|
* @param {DependencyList} deps - An array of dependencies that define when the callback should be re-executed. The `callback` is re-executed whenever the `deps` change.
|
|
11
11
|
*
|
|
12
|
-
* @returns {(element:
|
|
12
|
+
* @returns {(element: RefElement | null) => void} A function to set the element. Pass this function to the `ref` attribute, and the `callback` will be called whenever the element changes.
|
|
13
13
|
*
|
|
14
14
|
* @example
|
|
15
15
|
* import { useRefEffect } from 'react-simplikit';
|
|
@@ -26,6 +26,6 @@ type CleanupCallback = () => void;
|
|
|
26
26
|
* return <div ref={ref}>Basic Example</div>;
|
|
27
27
|
* }
|
|
28
28
|
*/
|
|
29
|
-
declare function useRefEffect<
|
|
29
|
+
declare function useRefEffect<RefElement extends HTMLElement = HTMLElement>(callback: (element: RefElement) => CleanupCallback | void, deps: DependencyList): (element: RefElement | null) => void;
|
|
30
30
|
|
|
31
31
|
export { useRefEffect };
|
|
@@ -7,9 +7,12 @@ import { useCallback as useCallback2, useRef as useRef2 } from "react";
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
type SetOrValues<T> = Set<T> | T[];
|
|
2
|
+
type SetActions<T> = {
|
|
3
|
+
add: (value: T) => void;
|
|
4
|
+
remove: (value: T) => void;
|
|
5
|
+
toggle: (value: T) => void;
|
|
6
|
+
setAll: (values: SetOrValues<T>) => void;
|
|
7
|
+
reset: () => void;
|
|
8
|
+
};
|
|
9
|
+
type UseSetReturn<T> = [Omit<Set<T>, 'add' | 'clear' | 'delete'>, SetActions<T>];
|
|
10
|
+
/**
|
|
11
|
+
* @description
|
|
12
|
+
* A React hook that manages a Set as state.
|
|
13
|
+
* Provides efficient state management and stable action functions.
|
|
14
|
+
*
|
|
15
|
+
* @param {SetOrValues<T>} initialState - Initial Set state (Set object or array of values)
|
|
16
|
+
* @returns {UseSetReturn<T>} A tuple containing the Set state and actions to manipulate it
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```tsx
|
|
20
|
+
* import { useSet } from 'react-simplikit';
|
|
21
|
+
*
|
|
22
|
+
* function TagSelector() {
|
|
23
|
+
* const [selectedTags, { add, remove, toggle }] = useSet<string>(['react']);
|
|
24
|
+
*
|
|
25
|
+
* return (
|
|
26
|
+
* <div>
|
|
27
|
+
* {['react', 'vue', 'svelte'].map(tag => (
|
|
28
|
+
* <button key={tag} onClick={() => toggle(tag)}>
|
|
29
|
+
* {selectedTags.has(tag) ? '✓' : ''} {tag}
|
|
30
|
+
* </button>
|
|
31
|
+
* ))}
|
|
32
|
+
* </div>
|
|
33
|
+
* );
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
declare function useSet<T>(initialState?: SetOrValues<T>): UseSetReturn<T>;
|
|
38
|
+
|
|
39
|
+
export { useSet };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/useSet/useSet.ts
|
|
4
|
+
import { useMemo as useMemo2, useState } from "react";
|
|
5
|
+
|
|
6
|
+
// src/hooks/usePreservedCallback/usePreservedCallback.ts
|
|
7
|
+
import { useCallback, useEffect, useRef } from "react";
|
|
8
|
+
function usePreservedCallback(callback) {
|
|
9
|
+
const callbackRef = useRef(callback);
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
16
|
+
return useCallback((...args) => {
|
|
17
|
+
return callbackRef.current(...args);
|
|
18
|
+
}, []);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// src/hooks/usePreservedReference/usePreservedReference.ts
|
|
22
|
+
import { useMemo, useRef as useRef2 } from "react";
|
|
23
|
+
function usePreservedReference(value, areValuesEqual = areDeeplyEqual) {
|
|
24
|
+
const ref = useRef2(value);
|
|
25
|
+
return useMemo(() => {
|
|
26
|
+
if (!areValuesEqual(ref.current, value)) {
|
|
27
|
+
ref.current = value;
|
|
28
|
+
}
|
|
29
|
+
return ref.current;
|
|
30
|
+
}, [areValuesEqual, value]);
|
|
31
|
+
}
|
|
32
|
+
function areDeeplyEqual(x, y) {
|
|
33
|
+
return JSON.stringify(x) === JSON.stringify(y);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/hooks/useSet/useSet.ts
|
|
37
|
+
function useSet(initialState = /* @__PURE__ */ new Set()) {
|
|
38
|
+
const [set, setSet] = useState(() => new Set(initialState));
|
|
39
|
+
const preservedInitialState = usePreservedReference(initialState);
|
|
40
|
+
const add = usePreservedCallback((value) => {
|
|
41
|
+
setSet((prev) => {
|
|
42
|
+
const nextSet = new Set(prev);
|
|
43
|
+
nextSet.add(value);
|
|
44
|
+
return nextSet;
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
const remove = usePreservedCallback((value) => {
|
|
48
|
+
setSet((prev) => {
|
|
49
|
+
const nextSet = new Set(prev);
|
|
50
|
+
nextSet.delete(value);
|
|
51
|
+
return nextSet;
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
const toggle = usePreservedCallback((value) => {
|
|
55
|
+
setSet((prev) => {
|
|
56
|
+
const nextSet = new Set(prev);
|
|
57
|
+
if (nextSet.has(value)) {
|
|
58
|
+
nextSet.delete(value);
|
|
59
|
+
} else {
|
|
60
|
+
nextSet.add(value);
|
|
61
|
+
}
|
|
62
|
+
return nextSet;
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
const setAll = usePreservedCallback((values) => {
|
|
66
|
+
setSet(() => new Set(values));
|
|
67
|
+
});
|
|
68
|
+
const reset = usePreservedCallback(() => {
|
|
69
|
+
setSet(() => new Set(preservedInitialState));
|
|
70
|
+
});
|
|
71
|
+
const actions = useMemo2(
|
|
72
|
+
() => ({ add, remove, toggle, setAll, reset }),
|
|
73
|
+
[add, remove, toggle, setAll, reset]
|
|
74
|
+
);
|
|
75
|
+
return [set, actions];
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
useSet
|
|
79
|
+
};
|
|
@@ -7,9 +7,12 @@ import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
|
|
|
7
7
|
import { useCallback, useEffect, useRef } from "react";
|
|
8
8
|
function usePreservedCallback(callback) {
|
|
9
9
|
const callbackRef = useRef(callback);
|
|
10
|
-
useEffect(
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
useEffect(
|
|
11
|
+
function syncCallbackRef() {
|
|
12
|
+
callbackRef.current = callback;
|
|
13
|
+
},
|
|
14
|
+
[callback]
|
|
15
|
+
);
|
|
13
16
|
return useCallback((...args) => {
|
|
14
17
|
return callbackRef.current(...args);
|
|
15
18
|
}, []);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
type ThrottleOptions = {
|
|
2
|
+
edges?: Array<'leading' | 'trailing'>;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* @description
|
|
6
|
+
* `useThrottledCallback` is a React hook that returns a throttled version of the provided callback function.
|
|
7
|
+
* The throttled callback will only be invoked at most once per specified interval.
|
|
8
|
+
*
|
|
9
|
+
* @param {Object} options - The options object.
|
|
10
|
+
* @param {Function} options.onChange - The callback function to throttle.
|
|
11
|
+
* @param {number} options.timeThreshold - The number of milliseconds to throttle invocations to.
|
|
12
|
+
* @param {Array<'leading' | 'trailing'>} [options.edges=['leading', 'trailing']] - An optional array specifying whether the function should be invoked on the leading edge, trailing edge, or both.
|
|
13
|
+
*
|
|
14
|
+
* @returns {Function} A throttled function that limits invoking the callback.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* function ScrollTracker() {
|
|
18
|
+
* const throttledScroll = useThrottledCallback({
|
|
19
|
+
* onChange: (scrollY: number) => console.log(scrollY),
|
|
20
|
+
* timeThreshold: 200,
|
|
21
|
+
* });
|
|
22
|
+
* return <div onScroll={(e) => throttledScroll(e.currentTarget.scrollTop)} />;
|
|
23
|
+
* }
|
|
24
|
+
*/
|
|
25
|
+
declare function useThrottledCallback({ onChange, timeThreshold, edges, }: ThrottleOptions & {
|
|
26
|
+
onChange: (newValue: boolean) => void;
|
|
27
|
+
timeThreshold: number;
|
|
28
|
+
}): (nextValue: boolean) => void;
|
|
29
|
+
|
|
30
|
+
export { useThrottledCallback };
|