@1money/hooks 0.1.0
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/README.md +53 -0
- package/es/index.d.ts +11 -0
- package/es/index.js +11 -0
- package/es/useControlledState/index.d.ts +14 -0
- package/es/useControlledState/index.js +36 -0
- package/es/useEventCallback/index.d.ts +11 -0
- package/es/useEventCallback/index.js +16 -0
- package/es/useLatest/index.d.ts +9 -0
- package/es/useLatest/index.js +15 -0
- package/es/useLayoutEffect/index.d.ts +11 -0
- package/es/useLayoutEffect/index.js +16 -0
- package/es/useLayoutState/index.d.ts +13 -0
- package/es/useLayoutState/index.js +76 -0
- package/es/useMemoizedFn/index.d.ts +12 -0
- package/es/useMemoizedFn/index.js +26 -0
- package/es/usePrevious/index.d.ts +11 -0
- package/es/usePrevious/index.js +19 -0
- package/es/useSafeState/index.d.ts +12 -0
- package/es/useSafeState/index.js +40 -0
- package/es/useSyncState/index.d.ts +14 -0
- package/es/useSyncState/index.js +43 -0
- package/es/useUpdateEffect/index.d.ts +9 -0
- package/es/useUpdateEffect/index.js +19 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.js +68 -0
- package/lib/useControlledState/index.d.ts +14 -0
- package/lib/useControlledState/index.js +54 -0
- package/lib/useEventCallback/index.d.ts +11 -0
- package/lib/useEventCallback/index.js +42 -0
- package/lib/useLatest/index.d.ts +9 -0
- package/lib/useLatest/index.js +30 -0
- package/lib/useLayoutEffect/index.d.ts +11 -0
- package/lib/useLayoutEffect/index.js +33 -0
- package/lib/useLayoutState/index.d.ts +13 -0
- package/lib/useLayoutState/index.js +85 -0
- package/lib/useMemoizedFn/index.d.ts +12 -0
- package/lib/useMemoizedFn/index.js +36 -0
- package/lib/usePrevious/index.d.ts +11 -0
- package/lib/usePrevious/index.js +32 -0
- package/lib/useSafeState/index.d.ts +12 -0
- package/lib/useSafeState/index.js +42 -0
- package/lib/useSyncState/index.d.ts +14 -0
- package/lib/useSyncState/index.js +54 -0
- package/lib/useUpdateEffect/index.d.ts +9 -0
- package/lib/useUpdateEffect/index.js +35 -0
- package/package.json +117 -0
package/README.md
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# @1money/hooks
|
|
2
|
+
|
|
3
|
+
React hooks for 1Money front-end projects.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @1money/hooks
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
> Requires `react >= 16.8.0` as a peer dependency.
|
|
12
|
+
|
|
13
|
+
## Hooks
|
|
14
|
+
|
|
15
|
+
| Hook | Description |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `useControlledState` | Manages state that can be either controlled (via props) or uncontrolled. |
|
|
18
|
+
| `useEventCallback` | Returns a stable callback reference that always invokes the latest function. |
|
|
19
|
+
| `useLatest` | Returns a ref that always holds the latest value, avoiding stale closures. |
|
|
20
|
+
| `useLayoutEffect` | Wrapper around `useLayoutEffect` that indicates whether it's the initial mount. |
|
|
21
|
+
| `useLayoutState` | Batches multiple state updates into a single microtask to reduce re-renders. |
|
|
22
|
+
| `useMemoizedFn` | Returns a memoized function with a stable reference across re-renders. |
|
|
23
|
+
| `usePrevious` | Returns the previous distinct value before the last change. |
|
|
24
|
+
| `useSafeState` | Like `useState` but prevents updates after unmount. |
|
|
25
|
+
| `useSyncState` | Returns a getter/setter where the getter always reflects the latest state synchronously. |
|
|
26
|
+
| `useUpdateEffect` | Like `useEffect` but skips the initial mount. |
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
import { useMemoizedFn, useLatest } from '@1money/hooks';
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Individual hooks can also be imported directly for tree-shaking:
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import useMemoizedFn from '@1money/hooks/useMemoizedFn';
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Development
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
pnpm install
|
|
44
|
+
pnpm dev # watch mode
|
|
45
|
+
pnpm build # production build
|
|
46
|
+
pnpm test # run tests
|
|
47
|
+
pnpm lint # check formatting & linting
|
|
48
|
+
pnpm lint:fix # auto-fix
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## License
|
|
52
|
+
|
|
53
|
+
MIT
|
package/es/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as useControlledState } from './useControlledState';
|
|
2
|
+
export { default as useEventCallback } from './useEventCallback';
|
|
3
|
+
export { default as useLatest } from './useLatest';
|
|
4
|
+
export { default as useLayoutEffect } from './useLayoutEffect';
|
|
5
|
+
export { default as useMemoizedFn } from './useMemoizedFn';
|
|
6
|
+
export { default as usePrevious } from './usePrevious';
|
|
7
|
+
export { default as useSafeState } from './useSafeState';
|
|
8
|
+
export { default as useSyncState } from './useSyncState';
|
|
9
|
+
export { default as useUpdateEffect } from './useUpdateEffect';
|
|
10
|
+
export { default as useLayoutState, useTimeoutLock } from './useLayoutState';
|
|
11
|
+
export type { Updater } from './useLayoutState';
|
package/es/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Hooks
|
|
2
|
+
export { default as useControlledState } from "./useControlledState";
|
|
3
|
+
export { default as useEventCallback } from "./useEventCallback";
|
|
4
|
+
export { default as useLatest } from "./useLatest";
|
|
5
|
+
export { default as useLayoutEffect } from "./useLayoutEffect";
|
|
6
|
+
export { default as useMemoizedFn } from "./useMemoizedFn";
|
|
7
|
+
export { default as usePrevious } from "./usePrevious";
|
|
8
|
+
export { default as useSafeState } from "./useSafeState";
|
|
9
|
+
export { default as useSyncState } from "./useSyncState";
|
|
10
|
+
export { default as useUpdateEffect } from "./useUpdateEffect";
|
|
11
|
+
export { default as useLayoutState, useTimeoutLock } from "./useLayoutState";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Updater<T> = (updater: T | ((origin: T) => T)) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Similar to `useState` but will use props value if provided.
|
|
4
|
+
* From React 18, we do not need safe `useState` since it will not throw for unmounted update.
|
|
5
|
+
* This hook removes the `onChange` & `postState` logic since we only need basic merged state logic.
|
|
6
|
+
*
|
|
7
|
+
* The `mergedValue` return already reflects the latest controlled `value` without
|
|
8
|
+
* the layout effect. The effect exists to sync `innerValue` for the edge case where
|
|
9
|
+
* the component starts in uncontrolled mode (calling `setInnerValue` directly) and
|
|
10
|
+
* then transitions to controlled mode (parent starts providing `value`). Without this
|
|
11
|
+
* sync, reverting back to uncontrolled mode would show the stale `innerValue`.
|
|
12
|
+
*/
|
|
13
|
+
export default function useControlledState<T>(defaultStateValue: T | (() => T), value?: T): [T, Updater<T>];
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { useState } from 'react';
|
|
8
|
+
import useLayoutEffect from "../useLayoutEffect";
|
|
9
|
+
/**
|
|
10
|
+
* Similar to `useState` but will use props value if provided.
|
|
11
|
+
* From React 18, we do not need safe `useState` since it will not throw for unmounted update.
|
|
12
|
+
* This hook removes the `onChange` & `postState` logic since we only need basic merged state logic.
|
|
13
|
+
*
|
|
14
|
+
* The `mergedValue` return already reflects the latest controlled `value` without
|
|
15
|
+
* the layout effect. The effect exists to sync `innerValue` for the edge case where
|
|
16
|
+
* the component starts in uncontrolled mode (calling `setInnerValue` directly) and
|
|
17
|
+
* then transitions to controlled mode (parent starts providing `value`). Without this
|
|
18
|
+
* sync, reverting back to uncontrolled mode would show the stale `innerValue`.
|
|
19
|
+
*/
|
|
20
|
+
export default function useControlledState(defaultStateValue, value) {
|
|
21
|
+
var _useState = useState(defaultStateValue),
|
|
22
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
23
|
+
innerValue = _useState2[0],
|
|
24
|
+
setInnerValue = _useState2[1];
|
|
25
|
+
var mergedValue = value !== undefined ? value : innerValue;
|
|
26
|
+
useLayoutEffect(function (mount) {
|
|
27
|
+
if (!mount && value !== undefined) {
|
|
28
|
+
setInnerValue(value);
|
|
29
|
+
}
|
|
30
|
+
}, [value]);
|
|
31
|
+
return [
|
|
32
|
+
// Value
|
|
33
|
+
mergedValue,
|
|
34
|
+
// Update function
|
|
35
|
+
setInnerValue];
|
|
36
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type Fn<T extends unknown[], R> = (...args: T) => R;
|
|
2
|
+
/**
|
|
3
|
+
* A hook that returns a stable callback reference that always invokes
|
|
4
|
+
* the latest version of the provided function. Similar to the React
|
|
5
|
+
* useEvent RFC proposal.
|
|
6
|
+
*
|
|
7
|
+
* @param fn The function to wrap
|
|
8
|
+
* @returns A stable callback that always calls the latest fn
|
|
9
|
+
*/
|
|
10
|
+
export default function useEventCallback<T extends unknown[], R>(fn: Fn<T, R>): Fn<T, R>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
import useLatest from "../useLatest";
|
|
3
|
+
/**
|
|
4
|
+
* A hook that returns a stable callback reference that always invokes
|
|
5
|
+
* the latest version of the provided function. Similar to the React
|
|
6
|
+
* useEvent RFC proposal.
|
|
7
|
+
*
|
|
8
|
+
* @param fn The function to wrap
|
|
9
|
+
* @returns A stable callback that always calls the latest fn
|
|
10
|
+
*/
|
|
11
|
+
export default function useEventCallback(fn) {
|
|
12
|
+
var fnRef = useLatest(fn);
|
|
13
|
+
return useCallback(function () {
|
|
14
|
+
return fnRef.current.apply(fnRef, arguments);
|
|
15
|
+
}, []);
|
|
16
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A hook that returns a ref object whose `.current` property is always
|
|
3
|
+
* updated to the latest value. Useful for accessing the latest value
|
|
4
|
+
* in callbacks without causing re-renders or stale closures.
|
|
5
|
+
*
|
|
6
|
+
* @param value The value to keep up-to-date
|
|
7
|
+
* @returns A ref object containing the latest value
|
|
8
|
+
*/
|
|
9
|
+
export default function useLatest<T>(value: T): import("react").RefObject<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A hook that returns a ref object whose `.current` property is always
|
|
5
|
+
* updated to the latest value. Useful for accessing the latest value
|
|
6
|
+
* in callbacks without causing re-renders or stale closures.
|
|
7
|
+
*
|
|
8
|
+
* @param value The value to keep up-to-date
|
|
9
|
+
* @returns A ref object containing the latest value
|
|
10
|
+
*/
|
|
11
|
+
export default function useLatest(value) {
|
|
12
|
+
var ref = useRef(value);
|
|
13
|
+
ref.current = value;
|
|
14
|
+
return ref;
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type DependencyList } from 'react';
|
|
2
|
+
type EffectCallback = (mount: boolean) => void | (() => void);
|
|
3
|
+
/**
|
|
4
|
+
* A custom `useLayoutEffect` that passes a boolean indicating whether
|
|
5
|
+
* it's the initial mount (`true`) or a subsequent update (`false`).
|
|
6
|
+
*
|
|
7
|
+
* @param effect The effect callback that receives a `mount` parameter
|
|
8
|
+
* @param deps The dependency array
|
|
9
|
+
*/
|
|
10
|
+
export default function useLayoutEffect(effect: EffectCallback, deps?: DependencyList): void;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { useLayoutEffect as reactUseLayoutEffect, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A custom `useLayoutEffect` that passes a boolean indicating whether
|
|
4
|
+
* it's the initial mount (`true`) or a subsequent update (`false`).
|
|
5
|
+
*
|
|
6
|
+
* @param effect The effect callback that receives a `mount` parameter
|
|
7
|
+
* @param deps The dependency array
|
|
8
|
+
*/
|
|
9
|
+
export default function useLayoutEffect(effect, deps) {
|
|
10
|
+
var isMounted = useRef(false);
|
|
11
|
+
reactUseLayoutEffect(function () {
|
|
12
|
+
var mount = !isMounted.current;
|
|
13
|
+
isMounted.current = true;
|
|
14
|
+
return effect(mount);
|
|
15
|
+
}, deps);
|
|
16
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type Updater<State> = (prev: State) => State;
|
|
2
|
+
/**
|
|
3
|
+
* Execute code before next frame but async.
|
|
4
|
+
*
|
|
5
|
+
* Batches multiple state updates into a single microtask (via `Promise.resolve`),
|
|
6
|
+
* reducing unnecessary re-renders when multiple updates happen synchronously.
|
|
7
|
+
*
|
|
8
|
+
* @param defaultState The initial state value
|
|
9
|
+
* @returns A tuple of `[state, setFrameState]`
|
|
10
|
+
*/
|
|
11
|
+
export default function useLayoutState<State>(defaultState: State): [State, (updater: Updater<State>) => void];
|
|
12
|
+
/** Lock frame, when frame pass reset the lock. */
|
|
13
|
+
export declare function useTimeoutLock<State>(defaultState?: State): [(state: State) => void, () => State | null];
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
8
|
+
/**
|
|
9
|
+
* Execute code before next frame but async.
|
|
10
|
+
*
|
|
11
|
+
* Batches multiple state updates into a single microtask (via `Promise.resolve`),
|
|
12
|
+
* reducing unnecessary re-renders when multiple updates happen synchronously.
|
|
13
|
+
*
|
|
14
|
+
* @param defaultState The initial state value
|
|
15
|
+
* @returns A tuple of `[state, setFrameState]`
|
|
16
|
+
*/
|
|
17
|
+
export default function useLayoutState(defaultState) {
|
|
18
|
+
var stateRef = useRef(defaultState);
|
|
19
|
+
var _useState = useState({}),
|
|
20
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
21
|
+
forceUpdate = _useState2[1];
|
|
22
|
+
var lastPromiseRef = useRef(null);
|
|
23
|
+
var updateBatchRef = useRef([]);
|
|
24
|
+
var setFrameState = useCallback(function (updater) {
|
|
25
|
+
updateBatchRef.current.push(updater);
|
|
26
|
+
var promise = Promise.resolve();
|
|
27
|
+
lastPromiseRef.current = promise;
|
|
28
|
+
promise.then(function () {
|
|
29
|
+
if (lastPromiseRef.current === promise) {
|
|
30
|
+
var prevBatch = updateBatchRef.current;
|
|
31
|
+
var prevState = stateRef.current;
|
|
32
|
+
updateBatchRef.current = [];
|
|
33
|
+
prevBatch.forEach(function (batchUpdater) {
|
|
34
|
+
stateRef.current = batchUpdater(stateRef.current);
|
|
35
|
+
});
|
|
36
|
+
lastPromiseRef.current = null;
|
|
37
|
+
if (prevState !== stateRef.current) {
|
|
38
|
+
forceUpdate({});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
}, []);
|
|
43
|
+
useEffect(function () {
|
|
44
|
+
return function () {
|
|
45
|
+
lastPromiseRef.current = null;
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
return [stateRef.current, setFrameState];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Lock frame, when frame pass reset the lock. */
|
|
52
|
+
export function useTimeoutLock(defaultState) {
|
|
53
|
+
var frameRef = useRef(defaultState !== null && defaultState !== void 0 ? defaultState : null);
|
|
54
|
+
var timeoutRef = useRef(null);
|
|
55
|
+
function cleanUp() {
|
|
56
|
+
if (timeoutRef.current != null) {
|
|
57
|
+
clearTimeout(timeoutRef.current);
|
|
58
|
+
timeoutRef.current = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function setState(newState) {
|
|
62
|
+
frameRef.current = newState;
|
|
63
|
+
cleanUp();
|
|
64
|
+
timeoutRef.current = setTimeout(function () {
|
|
65
|
+
frameRef.current = null;
|
|
66
|
+
timeoutRef.current = null;
|
|
67
|
+
}, 100);
|
|
68
|
+
}
|
|
69
|
+
function getState() {
|
|
70
|
+
return frameRef.current;
|
|
71
|
+
}
|
|
72
|
+
useEffect(function () {
|
|
73
|
+
return cleanUp;
|
|
74
|
+
}, []);
|
|
75
|
+
return [setState, getState];
|
|
76
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
type Fn<T extends unknown[], R> = (...args: T) => R;
|
|
2
|
+
/**
|
|
3
|
+
* A hook that returns a memoized version of a callback function.
|
|
4
|
+
* Unlike `useCallback`, it maintains referential equality across renders
|
|
5
|
+
* without requiring a dependency array, while always calling the latest
|
|
6
|
+
* version of the function.
|
|
7
|
+
*
|
|
8
|
+
* @param fn The function to memoize
|
|
9
|
+
* @returns A memoized function with stable reference
|
|
10
|
+
*/
|
|
11
|
+
export default function useMemoizedFn<T extends unknown[], R>(fn: Fn<T, R>): Fn<T, R>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useMemo, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A hook that returns a memoized version of a callback function.
|
|
4
|
+
* Unlike `useCallback`, it maintains referential equality across renders
|
|
5
|
+
* without requiring a dependency array, while always calling the latest
|
|
6
|
+
* version of the function.
|
|
7
|
+
*
|
|
8
|
+
* @param fn The function to memoize
|
|
9
|
+
* @returns A memoized function with stable reference
|
|
10
|
+
*/
|
|
11
|
+
export default function useMemoizedFn(fn) {
|
|
12
|
+
var fnRef = useRef(fn);
|
|
13
|
+
fnRef.current = useMemo(function () {
|
|
14
|
+
return fn;
|
|
15
|
+
}, [fn]);
|
|
16
|
+
var memoizedFn = useRef(undefined);
|
|
17
|
+
if (!memoizedFn.current) {
|
|
18
|
+
memoizedFn.current = function () {
|
|
19
|
+
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
|
20
|
+
args[_key] = arguments[_key];
|
|
21
|
+
}
|
|
22
|
+
return fnRef.current.apply(this, args);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return memoizedFn.current;
|
|
26
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns the last distinct value observed before the current `value`.
|
|
3
|
+
*
|
|
4
|
+
* Because the internal effect depends on `[value]`, the ref is only updated
|
|
5
|
+
* when `value` changes. That means this hook tracks the previous value on
|
|
6
|
+
* value-change boundaries (not every render).
|
|
7
|
+
*
|
|
8
|
+
* @param value The value to track
|
|
9
|
+
* @returns The previous distinct value, or `undefined` until one exists
|
|
10
|
+
*/
|
|
11
|
+
export default function usePrevious<T>(value: T): T | undefined;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Returns the last distinct value observed before the current `value`.
|
|
5
|
+
*
|
|
6
|
+
* Because the internal effect depends on `[value]`, the ref is only updated
|
|
7
|
+
* when `value` changes. That means this hook tracks the previous value on
|
|
8
|
+
* value-change boundaries (not every render).
|
|
9
|
+
*
|
|
10
|
+
* @param value The value to track
|
|
11
|
+
* @returns The previous distinct value, or `undefined` until one exists
|
|
12
|
+
*/
|
|
13
|
+
export default function usePrevious(value) {
|
|
14
|
+
var ref = useRef(undefined);
|
|
15
|
+
useEffect(function () {
|
|
16
|
+
ref.current = value;
|
|
17
|
+
}, [value]);
|
|
18
|
+
return ref.current;
|
|
19
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Dispatch, type SetStateAction } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* It is exactly the same as `React.useState`, but after the component is unmounted,
|
|
4
|
+
* the setState in the asynchronous callback will no longer be executed to avoid
|
|
5
|
+
* memory leakage caused by updating the state after the component is unmounted.
|
|
6
|
+
*
|
|
7
|
+
* @param initialState The initial state value or initializer function
|
|
8
|
+
* @returns A tuple of [state, setSafeState] similar to useState
|
|
9
|
+
*/
|
|
10
|
+
declare function useSafeState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];
|
|
11
|
+
declare function useSafeState<S = undefined>(): [S | undefined, Dispatch<SetStateAction<S | undefined>>];
|
|
12
|
+
export default useSafeState;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { useState, useCallback, useRef, useEffect } from 'react';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* It is exactly the same as `React.useState`, but after the component is unmounted,
|
|
11
|
+
* the setState in the asynchronous callback will no longer be executed to avoid
|
|
12
|
+
* memory leakage caused by updating the state after the component is unmounted.
|
|
13
|
+
*
|
|
14
|
+
* @param initialState The initial state value or initializer function
|
|
15
|
+
* @returns A tuple of [state, setSafeState] similar to useState
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// eslint-disable-next-line no-redeclare
|
|
19
|
+
|
|
20
|
+
// eslint-disable-next-line no-redeclare
|
|
21
|
+
function useSafeState(initialState) {
|
|
22
|
+
var isMountedRef = useRef(true);
|
|
23
|
+
var _useState = useState(initialState),
|
|
24
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
25
|
+
state = _useState2[0],
|
|
26
|
+
setState = _useState2[1];
|
|
27
|
+
useEffect(function () {
|
|
28
|
+
isMountedRef.current = true;
|
|
29
|
+
return function () {
|
|
30
|
+
isMountedRef.current = false;
|
|
31
|
+
};
|
|
32
|
+
}, []);
|
|
33
|
+
var setSafeState = useCallback(function (value) {
|
|
34
|
+
if (isMountedRef.current) {
|
|
35
|
+
setState(value);
|
|
36
|
+
}
|
|
37
|
+
}, []);
|
|
38
|
+
return [state, setSafeState];
|
|
39
|
+
}
|
|
40
|
+
export default useSafeState;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SetStateAction } from 'react';
|
|
2
|
+
type GetState<T> = () => T;
|
|
3
|
+
type SetState<T> = (value: SetStateAction<T>) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Same as `React.useState` but will always get the latest state.
|
|
6
|
+
* This is useful when React merges multiple state updates into one.
|
|
7
|
+
* e.g. `onTransitionEnd` triggers multiple events at once that will be merged
|
|
8
|
+
* into a single state update in React.
|
|
9
|
+
*
|
|
10
|
+
* @param initialState The initial state value or initializer function
|
|
11
|
+
* @returns A tuple of [getState, setState] where getState always returns the latest value
|
|
12
|
+
*/
|
|
13
|
+
export default function useSyncState<T>(initialState: T | (() => T)): [GetState<T>, SetState<T>];
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
|
|
2
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
|
|
3
|
+
function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
|
|
4
|
+
function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
|
|
5
|
+
function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
|
|
6
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
|
7
|
+
import { useState, useRef } from 'react';
|
|
8
|
+
import useEventCallback from "../useEventCallback";
|
|
9
|
+
/** Sentinel value distinguishing "not yet initialized" from any valid state, including `null`. */
|
|
10
|
+
var UNINITIALIZED = Symbol('useSyncState.uninitialized');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Same as `React.useState` but will always get the latest state.
|
|
14
|
+
* This is useful when React merges multiple state updates into one.
|
|
15
|
+
* e.g. `onTransitionEnd` triggers multiple events at once that will be merged
|
|
16
|
+
* into a single state update in React.
|
|
17
|
+
*
|
|
18
|
+
* @param initialState The initial state value or initializer function
|
|
19
|
+
* @returns A tuple of [getState, setState] where getState always returns the latest value
|
|
20
|
+
*/
|
|
21
|
+
export default function useSyncState(initialState) {
|
|
22
|
+
var stateRef = useRef(UNINITIALIZED);
|
|
23
|
+
|
|
24
|
+
// Initialize ref on first render
|
|
25
|
+
if (stateRef.current === UNINITIALIZED) {
|
|
26
|
+
stateRef.current = typeof initialState === 'function' ? initialState() : initialState;
|
|
27
|
+
}
|
|
28
|
+
var _useState = useState({}),
|
|
29
|
+
_useState2 = _slicedToArray(_useState, 2),
|
|
30
|
+
forceUpdate = _useState2[1];
|
|
31
|
+
var getState = useEventCallback(function () {
|
|
32
|
+
return stateRef.current;
|
|
33
|
+
});
|
|
34
|
+
var setState = useEventCallback(function (value) {
|
|
35
|
+
var prevState = stateRef.current;
|
|
36
|
+
var nextState = typeof value === 'function' ? value(prevState) : value;
|
|
37
|
+
if (!Object.is(prevState, nextState)) {
|
|
38
|
+
stateRef.current = nextState;
|
|
39
|
+
forceUpdate({});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
return [getState, setState];
|
|
43
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type DependencyList, type EffectCallback } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A hook identical to `useEffect`, but it skips the effect on the initial mount
|
|
4
|
+
* and only runs on subsequent updates.
|
|
5
|
+
*
|
|
6
|
+
* @param effect The effect callback to run on updates
|
|
7
|
+
* @param deps The dependency array
|
|
8
|
+
*/
|
|
9
|
+
export default function useUpdateEffect(effect: EffectCallback, deps?: DependencyList): void;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A hook identical to `useEffect`, but it skips the effect on the initial mount
|
|
5
|
+
* and only runs on subsequent updates.
|
|
6
|
+
*
|
|
7
|
+
* @param effect The effect callback to run on updates
|
|
8
|
+
* @param deps The dependency array
|
|
9
|
+
*/
|
|
10
|
+
export default function useUpdateEffect(effect, deps) {
|
|
11
|
+
var isMounted = useRef(false);
|
|
12
|
+
useEffect(function () {
|
|
13
|
+
if (!isMounted.current) {
|
|
14
|
+
isMounted.current = true;
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
return effect();
|
|
18
|
+
}, deps);
|
|
19
|
+
}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export { default as useControlledState } from './useControlledState';
|
|
2
|
+
export { default as useEventCallback } from './useEventCallback';
|
|
3
|
+
export { default as useLatest } from './useLatest';
|
|
4
|
+
export { default as useLayoutEffect } from './useLayoutEffect';
|
|
5
|
+
export { default as useMemoizedFn } from './useMemoizedFn';
|
|
6
|
+
export { default as usePrevious } from './usePrevious';
|
|
7
|
+
export { default as useSafeState } from './useSafeState';
|
|
8
|
+
export { default as useSyncState } from './useSyncState';
|
|
9
|
+
export { default as useUpdateEffect } from './useUpdateEffect';
|
|
10
|
+
export { default as useLayoutState, useTimeoutLock } from './useLayoutState';
|
|
11
|
+
export type { Updater } from './useLayoutState';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
20
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
21
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
22
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
23
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
24
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
25
|
+
mod
|
|
26
|
+
));
|
|
27
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
28
|
+
|
|
29
|
+
// src/index.ts
|
|
30
|
+
var src_exports = {};
|
|
31
|
+
__export(src_exports, {
|
|
32
|
+
useControlledState: () => import_useControlledState.default,
|
|
33
|
+
useEventCallback: () => import_useEventCallback.default,
|
|
34
|
+
useLatest: () => import_useLatest.default,
|
|
35
|
+
useLayoutEffect: () => import_useLayoutEffect.default,
|
|
36
|
+
useLayoutState: () => import_useLayoutState.default,
|
|
37
|
+
useMemoizedFn: () => import_useMemoizedFn.default,
|
|
38
|
+
usePrevious: () => import_usePrevious.default,
|
|
39
|
+
useSafeState: () => import_useSafeState.default,
|
|
40
|
+
useSyncState: () => import_useSyncState.default,
|
|
41
|
+
useTimeoutLock: () => import_useLayoutState.useTimeoutLock,
|
|
42
|
+
useUpdateEffect: () => import_useUpdateEffect.default
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(src_exports);
|
|
45
|
+
var import_useControlledState = __toESM(require("./useControlledState"));
|
|
46
|
+
var import_useEventCallback = __toESM(require("./useEventCallback"));
|
|
47
|
+
var import_useLatest = __toESM(require("./useLatest"));
|
|
48
|
+
var import_useLayoutEffect = __toESM(require("./useLayoutEffect"));
|
|
49
|
+
var import_useMemoizedFn = __toESM(require("./useMemoizedFn"));
|
|
50
|
+
var import_usePrevious = __toESM(require("./usePrevious"));
|
|
51
|
+
var import_useSafeState = __toESM(require("./useSafeState"));
|
|
52
|
+
var import_useSyncState = __toESM(require("./useSyncState"));
|
|
53
|
+
var import_useUpdateEffect = __toESM(require("./useUpdateEffect"));
|
|
54
|
+
var import_useLayoutState = __toESM(require("./useLayoutState"));
|
|
55
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
56
|
+
0 && (module.exports = {
|
|
57
|
+
useControlledState,
|
|
58
|
+
useEventCallback,
|
|
59
|
+
useLatest,
|
|
60
|
+
useLayoutEffect,
|
|
61
|
+
useLayoutState,
|
|
62
|
+
useMemoizedFn,
|
|
63
|
+
usePrevious,
|
|
64
|
+
useSafeState,
|
|
65
|
+
useSyncState,
|
|
66
|
+
useTimeoutLock,
|
|
67
|
+
useUpdateEffect
|
|
68
|
+
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
type Updater<T> = (updater: T | ((origin: T) => T)) => void;
|
|
2
|
+
/**
|
|
3
|
+
* Similar to `useState` but will use props value if provided.
|
|
4
|
+
* From React 18, we do not need safe `useState` since it will not throw for unmounted update.
|
|
5
|
+
* This hook removes the `onChange` & `postState` logic since we only need basic merged state logic.
|
|
6
|
+
*
|
|
7
|
+
* The `mergedValue` return already reflects the latest controlled `value` without
|
|
8
|
+
* the layout effect. The effect exists to sync `innerValue` for the edge case where
|
|
9
|
+
* the component starts in uncontrolled mode (calling `setInnerValue` directly) and
|
|
10
|
+
* then transitions to controlled mode (parent starts providing `value`). Without this
|
|
11
|
+
* sync, reverting back to uncontrolled mode would show the stale `innerValue`.
|
|
12
|
+
*/
|
|
13
|
+
export default function useControlledState<T>(defaultStateValue: T | (() => T), value?: T): [T, Updater<T>];
|
|
14
|
+
export {};
|