@aweebit/react-essentials 0.4.0 → 0.5.1
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/LICENSE +2 -0
- package/dist/hooks/useEventListener.d.ts +2 -21
- package/dist/hooks/useEventListener.d.ts.map +1 -1
- package/dist/hooks/useEventListener.js +2 -20
- package/dist/hooks/useEventListener.js.map +1 -0
- package/dist/hooks/useForceUpdate.d.ts +26 -4
- package/dist/hooks/useForceUpdate.d.ts.map +1 -1
- package/dist/hooks/useForceUpdate.js +37 -4
- package/dist/hooks/useForceUpdate.js.map +1 -0
- package/dist/hooks/useReducerWithDeps.d.ts +36 -0
- package/dist/hooks/useReducerWithDeps.d.ts.map +1 -0
- package/dist/hooks/useReducerWithDeps.js +46 -0
- package/dist/hooks/useReducerWithDeps.js.map +1 -0
- package/dist/hooks/useStateWithDeps.d.ts +23 -0
- package/dist/hooks/useStateWithDeps.d.ts.map +1 -0
- package/dist/hooks/useStateWithDeps.js +64 -0
- package/dist/hooks/useStateWithDeps.js.map +1 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -0
- package/dist/utils/index.d.ts +0 -0
- package/dist/utils/index.d.ts.map +0 -0
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -0
- package/lib/hooks/useEventListener.ts +2 -21
- package/lib/hooks/useForceUpdate.ts +38 -7
- package/lib/hooks/useReducerWithDeps.ts +59 -0
- package/lib/hooks/useStateWithDeps.ts +79 -0
- package/lib/index.ts +2 -1
- package/lib/utils/index.ts +0 -0
- package/package.json +5 -3
- package/dist/hooks/useDerivedState.d.ts +0 -42
- package/dist/hooks/useDerivedState.d.ts.map +0 -1
- package/dist/hooks/useDerivedState.js +0 -92
- package/lib/hooks/useDerivedState.ts +0 -108
package/LICENSE
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2025 Wee Bit
|
|
4
|
+
Portions Copyright (c) 2020 Peter Juras
|
|
5
|
+
Portions Copyright (c) 2020 Julien CARON
|
|
4
6
|
|
|
5
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
8
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -2,30 +2,11 @@
|
|
|
2
2
|
* @file Based on {@link https://github.com/juliencrn/usehooks-ts}
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2020 Julien CARON
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
5
|
+
* @copyright 2020 Julien CARON
|
|
25
6
|
*/
|
|
26
7
|
/**
|
|
27
8
|
* Adds `handler` as a listener for the event `eventName` of `element`
|
|
28
|
-
* (or `window` by default) with the provided `options` applied
|
|
9
|
+
* (or `window` by default) with the provided `options` applied
|
|
29
10
|
*
|
|
30
11
|
* It is the user's responsibility to make sure `element` and `options` values
|
|
31
12
|
* are correctly memoized!
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../lib/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../lib/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;;;;GAMG;AAGH,iBAAS,gBAAgB,CACvB,CAAC,SAAS,MAAM,kBAAkB,EAClC,CAAC,SAAS,UAAU,EAEpB,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KAAK,IAAI,EAC/C,OAAO,EAAE,CAAC,EACV,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;AAGR,iBAAS,gBAAgB,CACvB,CAAC,SAAS,MAAM,mBAAmB,EACnC,CAAC,SAAS,WAAW,EAErB,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,EAChD,OAAO,EAAE,CAAC,EACV,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;AAGR,iBAAS,gBAAgB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EACxD,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,EAC7C,OAAO,EAAE,QAAQ,EACjB,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;AAGR,iBAAS,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EACtD,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EAC3C,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;AAiCR,eAAe,gBAAgB,CAAC"}
|
|
@@ -2,26 +2,7 @@
|
|
|
2
2
|
* @file Based on {@link https://github.com/juliencrn/usehooks-ts}
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2020 Julien CARON
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
5
|
+
* @copyright 2020 Julien CARON
|
|
25
6
|
*/
|
|
26
7
|
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
27
8
|
function useEventListener(eventName, handler, element, options) {
|
|
@@ -45,3 +26,4 @@ function useEventListener(eventName, handler, element, options) {
|
|
|
45
26
|
}, [eventName, element, options]);
|
|
46
27
|
}
|
|
47
28
|
export default useEventListener;
|
|
29
|
+
//# sourceMappingURL=useEventListener.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../lib/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAgD3D,SAAS,gBAAgB,CACvB,SAAiB,EACjB,OAA+B,EAC/B,OAAqB,EACrB,OAA2C;IAE3C,mCAAmC;IACnC,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAErC,eAAe,CAAC,GAAG,EAAE;QACnB,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;IACjC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,SAAS,CAAC,GAAG,EAAE;QACb,8BAA8B;QAC9B,MAAM,aAAa,GAAG,OAAO,IAAI,MAAM,CAAC;QAExC,kEAAkE;QAClE,MAAM,QAAQ,GAAmB,CAAC,KAAK,EAAE,EAAE;YACzC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC9B,CAAC,CAAC;QAEF,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAE7D,mCAAmC;QACnC,OAAO,GAAG,EAAE;YACV,aAAa,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClE,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,eAAe,gBAAgB,CAAC"}
|
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Enables you to imperatively trigger re-rendering of components
|
|
3
|
+
*
|
|
4
|
+
* This hook is designed in the most general way possible in order to cover all
|
|
5
|
+
* imaginable use cases.
|
|
6
|
+
*
|
|
7
|
+
* @param callback An optional callback function to call during renders that
|
|
8
|
+
* were triggered with `forceUpdate()`
|
|
9
|
+
*
|
|
10
|
+
* Can be used for conditionally calling state setters when state needs to be
|
|
11
|
+
* reset. That is legal and better than using effects (see
|
|
12
|
+
* {@link https://react.dev/learn/-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes You Might Not Need an Effect > Adjusting some state when a prop changes}),
|
|
13
|
+
* but can often be avoided by using
|
|
14
|
+
* [`useStateWithDeps`]({@link ./useStateWithDeps.ts}) or
|
|
15
|
+
* [`useReducerWithDeps`]({@link ./useReducerWithDeps.ts}).
|
|
16
|
+
*
|
|
17
|
+
* Important: the callback function is called once per render, not once per
|
|
18
|
+
* `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
|
|
19
|
+
* be called once.
|
|
20
|
+
*
|
|
21
|
+
* @returns An array with the following two elements:
|
|
22
|
+
*
|
|
23
|
+
* 1. A `forceUpdate` function that triggers a re-render
|
|
24
|
+
* 2. The number of times `forceUpdate` has been called so far
|
|
25
|
+
*/
|
|
26
|
+
export default function useForceUpdate(callback?: () => void): [() => void, bigint];
|
|
5
27
|
//# sourceMappingURL=useForceUpdate.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useForceUpdate.d.ts","sourceRoot":"","sources":["../../lib/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,OAAO,UAAU,cAAc,
|
|
1
|
+
{"version":3,"file":"useForceUpdate.d.ts","sourceRoot":"","sources":["../../lib/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,QAAQ,CAAC,EAAE,MAAM,IAAI,GACpB,CAAC,MAAM,IAAI,EAAE,MAAM,CAAC,CAUtB"}
|
|
@@ -1,5 +1,38 @@
|
|
|
1
|
-
import { useReducer } from 'react';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { useReducer, useRef } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Enables you to imperatively trigger re-rendering of components
|
|
4
|
+
*
|
|
5
|
+
* This hook is designed in the most general way possible in order to cover all
|
|
6
|
+
* imaginable use cases.
|
|
7
|
+
*
|
|
8
|
+
* @param callback An optional callback function to call during renders that
|
|
9
|
+
* were triggered with `forceUpdate()`
|
|
10
|
+
*
|
|
11
|
+
* Can be used for conditionally calling state setters when state needs to be
|
|
12
|
+
* reset. That is legal and better than using effects (see
|
|
13
|
+
* {@link https://react.dev/learn/-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes You Might Not Need an Effect > Adjusting some state when a prop changes}),
|
|
14
|
+
* but can often be avoided by using
|
|
15
|
+
* [`useStateWithDeps`]({@link ./useStateWithDeps.ts}) or
|
|
16
|
+
* [`useReducerWithDeps`]({@link ./useReducerWithDeps.ts}).
|
|
17
|
+
*
|
|
18
|
+
* Important: the callback function is called once per render, not once per
|
|
19
|
+
* `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
|
|
20
|
+
* be called once.
|
|
21
|
+
*
|
|
22
|
+
* @returns An array with the following two elements:
|
|
23
|
+
*
|
|
24
|
+
* 1. A `forceUpdate` function that triggers a re-render
|
|
25
|
+
* 2. The number of times `forceUpdate` has been called so far
|
|
26
|
+
*/
|
|
27
|
+
export default function useForceUpdate(callback) {
|
|
28
|
+
// It is very unlikely that the number of updates will exceed
|
|
29
|
+
// Number.MAX_SAFE_INTEGER, but not impossible. That is why we use bigints.
|
|
30
|
+
const [counter, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
|
|
31
|
+
const counterRef = useRef(counter);
|
|
32
|
+
if (counter !== counterRef.current) {
|
|
33
|
+
counterRef.current = counter;
|
|
34
|
+
callback?.();
|
|
35
|
+
}
|
|
36
|
+
return [forceUpdate, counter];
|
|
5
37
|
}
|
|
38
|
+
//# sourceMappingURL=useForceUpdate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useForceUpdate.js","sourceRoot":"","sources":["../../lib/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CACpC,QAAqB;IAErB,6DAA6D;IAC7D,2EAA2E;IAC3E,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,OAAO,KAAK,UAAU,CAAC,OAAO,EAAE,CAAC;QACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;QAC7B,QAAQ,EAAE,EAAE,CAAC;IACf,CAAC;IACD,OAAO,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type ActionDispatch, type AnyActionArg, type DependencyList } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* `useReducer` hook with an additional dependency array that resets the state
|
|
4
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
5
|
+
* change
|
|
6
|
+
*
|
|
7
|
+
* ### On linter support
|
|
8
|
+
*
|
|
9
|
+
* The `react-hooks/exhaustive-deps` ESLint rule doesn't support hooks where
|
|
10
|
+
* the dependency array parameter is at any other position than the second.
|
|
11
|
+
* However, as we would like to keep the hook as compatible with `useReducer` as
|
|
12
|
+
* possible, we don't want to artificially change the parameter's position.
|
|
13
|
+
* Therefore, there will be no warnings about missing dependencies.
|
|
14
|
+
* Because of that, addition caution is advised!
|
|
15
|
+
* Be sure to check no dependencies are missing from the `deps` array.
|
|
16
|
+
*
|
|
17
|
+
* Related issue: {@link https://github.com/facebook/react/issues/25443}.
|
|
18
|
+
*
|
|
19
|
+
* Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
|
|
20
|
+
* `useExhaustiveDependencies` rule provided for Biome by Biome's team
|
|
21
|
+
* does actually have support for dependency arrays at other positions, see
|
|
22
|
+
* {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
|
|
23
|
+
*
|
|
24
|
+
* @param reducer The reducer function that specifies how the state gets updated
|
|
25
|
+
*
|
|
26
|
+
* @param initialState The state that will be set when the component mounts or
|
|
27
|
+
* the dependencies change
|
|
28
|
+
*
|
|
29
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
30
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
31
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
32
|
+
*
|
|
33
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
34
|
+
*/
|
|
35
|
+
export default function useReducerWithDeps<S, A extends AnyActionArg>(reducer: (prevState: S, ...args: A) => S, initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, ActionDispatch<A>];
|
|
36
|
+
//# sourceMappingURL=useReducerWithDeps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useReducerWithDeps.d.ts","sourceRoot":"","sources":["../../lib/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,cAAc,EAGpB,MAAM,OAAO,CAAC;AAGf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CAAC,CAAC,EAAE,CAAC,SAAS,YAAY,EAClE,OAAO,EAAE,CAAC,SAAS,EAAE,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,KAAK,CAAC,EACxC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAC5C,IAAI,EAAE,cAAc,GACnB,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAYxB"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { useCallback, useRef, } from 'react';
|
|
2
|
+
import useStateWithDeps from './useStateWithDeps';
|
|
3
|
+
/**
|
|
4
|
+
* `useReducer` hook with an additional dependency array that resets the state
|
|
5
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
6
|
+
* change
|
|
7
|
+
*
|
|
8
|
+
* ### On linter support
|
|
9
|
+
*
|
|
10
|
+
* The `react-hooks/exhaustive-deps` ESLint rule doesn't support hooks where
|
|
11
|
+
* the dependency array parameter is at any other position than the second.
|
|
12
|
+
* However, as we would like to keep the hook as compatible with `useReducer` as
|
|
13
|
+
* possible, we don't want to artificially change the parameter's position.
|
|
14
|
+
* Therefore, there will be no warnings about missing dependencies.
|
|
15
|
+
* Because of that, addition caution is advised!
|
|
16
|
+
* Be sure to check no dependencies are missing from the `deps` array.
|
|
17
|
+
*
|
|
18
|
+
* Related issue: {@link https://github.com/facebook/react/issues/25443}.
|
|
19
|
+
*
|
|
20
|
+
* Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
|
|
21
|
+
* `useExhaustiveDependencies` rule provided for Biome by Biome's team
|
|
22
|
+
* does actually have support for dependency arrays at other positions, see
|
|
23
|
+
* {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
|
|
24
|
+
*
|
|
25
|
+
* @param reducer The reducer function that specifies how the state gets updated
|
|
26
|
+
*
|
|
27
|
+
* @param initialState The state that will be set when the component mounts or
|
|
28
|
+
* the dependencies change
|
|
29
|
+
*
|
|
30
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
31
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
32
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
33
|
+
*
|
|
34
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
35
|
+
*/
|
|
36
|
+
export default function useReducerWithDeps(reducer, initialState, deps) {
|
|
37
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
38
|
+
const [state, setState] = useStateWithDeps(initialState, deps);
|
|
39
|
+
// Only the initially provided reducer is used
|
|
40
|
+
const reducerRef = useRef(reducer);
|
|
41
|
+
const dispatch = useCallback(function dispatch(...args) {
|
|
42
|
+
setState((previousState) => reducerRef.current(previousState, ...args));
|
|
43
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
44
|
+
return [state, dispatch];
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=useReducerWithDeps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useReducerWithDeps.js","sourceRoot":"","sources":["../../lib/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,EACX,MAAM,GACP,MAAM,OAAO,CAAC;AACf,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,CAAC,OAAO,UAAU,kBAAkB,CACxC,OAAwC,EACxC,YAA4C,EAC5C,IAAoB;IAEpB,uDAAuD;IACvD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,gBAAgB,CAAC,YAAY,EAAE,IAAI,CAAC,CAAC;IAE/D,8CAA8C;IAC9C,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAEnC,MAAM,QAAQ,GAAG,WAAW,CAAC,SAAS,QAAQ,CAAC,GAAG,IAAO;QACvD,QAAQ,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kDAAkD;IAE1D,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC3B,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright 2020 Peter Juras
|
|
6
|
+
*/
|
|
7
|
+
import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
|
|
8
|
+
/**
|
|
9
|
+
* `useState` hook with an additional dependency array that resets the state
|
|
10
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
11
|
+
* change
|
|
12
|
+
*
|
|
13
|
+
* @param initialState The state that will be set when the component mounts or
|
|
14
|
+
* the dependencies change
|
|
15
|
+
*
|
|
16
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
17
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
18
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
19
|
+
*
|
|
20
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
21
|
+
*/
|
|
22
|
+
export default function useStateWithDeps<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
|
|
23
|
+
//# sourceMappingURL=useStateWithDeps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../lib/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAIf;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,CAAC,EACxC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAC5C,IAAI,EAAE,cAAc,GACnB,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CA4ClC"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright 2020 Peter Juras
|
|
6
|
+
*/
|
|
7
|
+
import { useCallback, useRef, } from 'react';
|
|
8
|
+
import { depsAreEqual, isFunction } from '../utils';
|
|
9
|
+
import useForceUpdate from './useForceUpdate';
|
|
10
|
+
/**
|
|
11
|
+
* `useState` hook with an additional dependency array that resets the state
|
|
12
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
13
|
+
* change
|
|
14
|
+
*
|
|
15
|
+
* @param initialState The state that will be set when the component mounts or
|
|
16
|
+
* the dependencies change
|
|
17
|
+
*
|
|
18
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
19
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
20
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
21
|
+
*
|
|
22
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
23
|
+
*/
|
|
24
|
+
export default function useStateWithDeps(initialState, deps) {
|
|
25
|
+
// It would be possible to use useState instead of
|
|
26
|
+
// useRef to store the state, however this would
|
|
27
|
+
// trigger re-renders whenever the state is reset due
|
|
28
|
+
// to a change in dependencies. In order to avoid these
|
|
29
|
+
// re-renders, the state is stored in a ref and an
|
|
30
|
+
// update is triggered via forceUpdate below when necessary
|
|
31
|
+
const state = useRef(undefined);
|
|
32
|
+
const prevDeps = useRef(deps);
|
|
33
|
+
const isMounted = useRef(false);
|
|
34
|
+
// If first render, or if dependencies have changed since last time
|
|
35
|
+
if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
|
|
36
|
+
// Update state and deps
|
|
37
|
+
let nextState;
|
|
38
|
+
if (isFunction(initialState)) {
|
|
39
|
+
nextState = initialState(state.current);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
nextState = initialState;
|
|
43
|
+
}
|
|
44
|
+
state.current = nextState;
|
|
45
|
+
prevDeps.current = deps;
|
|
46
|
+
isMounted.current = true;
|
|
47
|
+
}
|
|
48
|
+
const [forceUpdate] = useForceUpdate();
|
|
49
|
+
const updateState = useCallback(function updateState(newState) {
|
|
50
|
+
let nextState;
|
|
51
|
+
if (isFunction(newState)) {
|
|
52
|
+
nextState = newState(state.current);
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
nextState = newState;
|
|
56
|
+
}
|
|
57
|
+
if (!Object.is(state.current, nextState)) {
|
|
58
|
+
state.current = nextState;
|
|
59
|
+
forceUpdate();
|
|
60
|
+
}
|
|
61
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
62
|
+
return [state.current, updateState];
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=useStateWithDeps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateWithDeps.js","sourceRoot":"","sources":["../../lib/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,WAAW,EACX,MAAM,GAIP,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AACpD,OAAO,cAAc,MAAM,kBAAkB,CAAC;AAE9C;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,UAAU,gBAAgB,CACtC,YAA4C,EAC5C,IAAoB;IAEpB,kDAAkD;IAClD,gDAAgD;IAChD,qDAAqD;IACrD,uDAAuD;IACvD,kDAAkD;IAClD,2DAA2D;IAC3D,MAAM,KAAK,GAAG,MAAM,CAAC,SAAc,CAAC,CAAC;IAErC,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAEhC,mEAAmE;IACnE,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,CAAC;QAChE,wBAAwB;QACxB,IAAI,SAAY,CAAC;QACjB,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC7B,SAAS,GAAG,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1C,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,YAAY,CAAC;QAC3B,CAAC;QACD,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;QAC1B,QAAQ,CAAC,OAAO,GAAG,IAAI,CAAC;QACxB,SAAS,CAAC,OAAO,GAAG,IAAI,CAAC;IAC3B,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,GAAG,cAAc,EAAE,CAAC;IAEvC,MAAM,WAAW,GAAG,WAAW,CAAC,SAAS,WAAW,CAClD,QAAuC;QAEvC,IAAI,SAAY,CAAC;QACjB,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACtC,CAAC;aAAM,CAAC;YACN,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC;YACzC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC;YAC1B,WAAW,EAAE,CAAC;QAChB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kDAAkD;IAE1D,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;AACtC,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
export { default as useDerivedState } from './hooks/useDerivedState';
|
|
2
1
|
export { default as useEventListener } from './hooks/useEventListener';
|
|
3
2
|
export { default as useForceUpdate } from './hooks/useForceUpdate';
|
|
3
|
+
export { default as useReducerWithDeps } from './hooks/useReducerWithDeps';
|
|
4
|
+
export { default as useStateWithDeps } from './hooks/useStateWithDeps';
|
|
4
5
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
export { default as useDerivedState } from './hooks/useDerivedState';
|
|
2
1
|
export { default as useEventListener } from './hooks/useEventListener';
|
|
3
2
|
export { default as useForceUpdate } from './hooks/useForceUpdate';
|
|
3
|
+
export { default as useReducerWithDeps } from './hooks/useReducerWithDeps';
|
|
4
|
+
export { default as useStateWithDeps } from './hooks/useStateWithDeps';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,0BAA0B,CAAC"}
|
package/dist/utils/index.d.ts
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/utils/index.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAEA,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAwB,EACxB,IAAoB;IAEpB,OAAO,CACL,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAC/B,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5D,CAAC;AACJ,CAAC"}
|
|
@@ -2,33 +2,14 @@
|
|
|
2
2
|
* @file Based on {@link https://github.com/juliencrn/usehooks-ts}
|
|
3
3
|
*
|
|
4
4
|
* @license MIT
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2020 Julien CARON
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
5
|
+
* @copyright 2020 Julien CARON
|
|
25
6
|
*/
|
|
26
7
|
|
|
27
8
|
import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
28
9
|
|
|
29
10
|
/**
|
|
30
11
|
* Adds `handler` as a listener for the event `eventName` of `element`
|
|
31
|
-
* (or `window` by default) with the provided `options` applied
|
|
12
|
+
* (or `window` by default) with the provided `options` applied
|
|
32
13
|
*
|
|
33
14
|
* It is the user's responsibility to make sure `element` and `options` values
|
|
34
15
|
* are correctly memoized!
|
|
@@ -1,9 +1,40 @@
|
|
|
1
|
-
import { useReducer } from 'react';
|
|
1
|
+
import { useReducer, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Enables you to imperatively trigger re-rendering of components
|
|
5
|
+
*
|
|
6
|
+
* This hook is designed in the most general way possible in order to cover all
|
|
7
|
+
* imaginable use cases.
|
|
8
|
+
*
|
|
9
|
+
* @param callback An optional callback function to call during renders that
|
|
10
|
+
* were triggered with `forceUpdate()`
|
|
11
|
+
*
|
|
12
|
+
* Can be used for conditionally calling state setters when state needs to be
|
|
13
|
+
* reset. That is legal and better than using effects (see
|
|
14
|
+
* {@link https://react.dev/learn/-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes You Might Not Need an Effect > Adjusting some state when a prop changes}),
|
|
15
|
+
* but can often be avoided by using
|
|
16
|
+
* [`useStateWithDeps`]({@link ./useStateWithDeps.ts}) or
|
|
17
|
+
* [`useReducerWithDeps`]({@link ./useReducerWithDeps.ts}).
|
|
18
|
+
*
|
|
19
|
+
* Important: the callback function is called once per render, not once per
|
|
20
|
+
* `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
|
|
21
|
+
* be called once.
|
|
22
|
+
*
|
|
23
|
+
* @returns An array with the following two elements:
|
|
24
|
+
*
|
|
25
|
+
* 1. A `forceUpdate` function that triggers a re-render
|
|
26
|
+
* 2. The number of times `forceUpdate` has been called so far
|
|
27
|
+
*/
|
|
28
|
+
export default function useForceUpdate(
|
|
29
|
+
callback?: () => void,
|
|
30
|
+
): [() => void, bigint] {
|
|
31
|
+
// It is very unlikely that the number of updates will exceed
|
|
32
|
+
// Number.MAX_SAFE_INTEGER, but not impossible. That is why we use bigints.
|
|
33
|
+
const [counter, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
|
|
34
|
+
const counterRef = useRef(counter);
|
|
35
|
+
if (counter !== counterRef.current) {
|
|
36
|
+
counterRef.current = counter;
|
|
37
|
+
callback?.();
|
|
38
|
+
}
|
|
39
|
+
return [forceUpdate, counter];
|
|
9
40
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ActionDispatch,
|
|
3
|
+
type AnyActionArg,
|
|
4
|
+
type DependencyList,
|
|
5
|
+
useCallback,
|
|
6
|
+
useRef,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import useStateWithDeps from './useStateWithDeps';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `useReducer` hook with an additional dependency array that resets the state
|
|
12
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
13
|
+
* change
|
|
14
|
+
*
|
|
15
|
+
* ### On linter support
|
|
16
|
+
*
|
|
17
|
+
* The `react-hooks/exhaustive-deps` ESLint rule doesn't support hooks where
|
|
18
|
+
* the dependency array parameter is at any other position than the second.
|
|
19
|
+
* However, as we would like to keep the hook as compatible with `useReducer` as
|
|
20
|
+
* possible, we don't want to artificially change the parameter's position.
|
|
21
|
+
* Therefore, there will be no warnings about missing dependencies.
|
|
22
|
+
* Because of that, addition caution is advised!
|
|
23
|
+
* Be sure to check no dependencies are missing from the `deps` array.
|
|
24
|
+
*
|
|
25
|
+
* Related issue: {@link https://github.com/facebook/react/issues/25443}.
|
|
26
|
+
*
|
|
27
|
+
* Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
|
|
28
|
+
* `useExhaustiveDependencies` rule provided for Biome by Biome's team
|
|
29
|
+
* does actually have support for dependency arrays at other positions, see
|
|
30
|
+
* {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
|
|
31
|
+
*
|
|
32
|
+
* @param reducer The reducer function that specifies how the state gets updated
|
|
33
|
+
*
|
|
34
|
+
* @param initialState The state that will be set when the component mounts or
|
|
35
|
+
* the dependencies change
|
|
36
|
+
*
|
|
37
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
38
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
39
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
40
|
+
*
|
|
41
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
42
|
+
*/
|
|
43
|
+
export default function useReducerWithDeps<S, A extends AnyActionArg>(
|
|
44
|
+
reducer: (prevState: S, ...args: A) => S,
|
|
45
|
+
initialState: S | ((previousState?: S) => S),
|
|
46
|
+
deps: DependencyList,
|
|
47
|
+
): [S, ActionDispatch<A>] {
|
|
48
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
|
+
const [state, setState] = useStateWithDeps(initialState, deps);
|
|
50
|
+
|
|
51
|
+
// Only the initially provided reducer is used
|
|
52
|
+
const reducerRef = useRef(reducer);
|
|
53
|
+
|
|
54
|
+
const dispatch = useCallback(function dispatch(...args: A): void {
|
|
55
|
+
setState((previousState) => reducerRef.current(previousState, ...args));
|
|
56
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
57
|
+
|
|
58
|
+
return [state, dispatch];
|
|
59
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright 2020 Peter Juras
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
useCallback,
|
|
10
|
+
useRef,
|
|
11
|
+
type DependencyList,
|
|
12
|
+
type Dispatch,
|
|
13
|
+
type SetStateAction,
|
|
14
|
+
} from 'react';
|
|
15
|
+
import { depsAreEqual, isFunction } from '../utils';
|
|
16
|
+
import useForceUpdate from './useForceUpdate';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* `useState` hook with an additional dependency array that resets the state
|
|
20
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
21
|
+
* change
|
|
22
|
+
*
|
|
23
|
+
* @param initialState The state that will be set when the component mounts or
|
|
24
|
+
* the dependencies change
|
|
25
|
+
*
|
|
26
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
27
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
28
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
29
|
+
*
|
|
30
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
31
|
+
*/
|
|
32
|
+
export default function useStateWithDeps<S>(
|
|
33
|
+
initialState: S | ((previousState?: S) => S),
|
|
34
|
+
deps: DependencyList,
|
|
35
|
+
): [S, Dispatch<SetStateAction<S>>] {
|
|
36
|
+
// It would be possible to use useState instead of
|
|
37
|
+
// useRef to store the state, however this would
|
|
38
|
+
// trigger re-renders whenever the state is reset due
|
|
39
|
+
// to a change in dependencies. In order to avoid these
|
|
40
|
+
// re-renders, the state is stored in a ref and an
|
|
41
|
+
// update is triggered via forceUpdate below when necessary
|
|
42
|
+
const state = useRef(undefined as S);
|
|
43
|
+
|
|
44
|
+
const prevDeps = useRef(deps);
|
|
45
|
+
const isMounted = useRef(false);
|
|
46
|
+
|
|
47
|
+
// If first render, or if dependencies have changed since last time
|
|
48
|
+
if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
|
|
49
|
+
// Update state and deps
|
|
50
|
+
let nextState: S;
|
|
51
|
+
if (isFunction(initialState)) {
|
|
52
|
+
nextState = initialState(state.current);
|
|
53
|
+
} else {
|
|
54
|
+
nextState = initialState;
|
|
55
|
+
}
|
|
56
|
+
state.current = nextState;
|
|
57
|
+
prevDeps.current = deps;
|
|
58
|
+
isMounted.current = true;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const [forceUpdate] = useForceUpdate();
|
|
62
|
+
|
|
63
|
+
const updateState = useCallback(function updateState(
|
|
64
|
+
newState: S | ((previousState: S) => S),
|
|
65
|
+
): void {
|
|
66
|
+
let nextState: S;
|
|
67
|
+
if (isFunction(newState)) {
|
|
68
|
+
nextState = newState(state.current);
|
|
69
|
+
} else {
|
|
70
|
+
nextState = newState;
|
|
71
|
+
}
|
|
72
|
+
if (!Object.is(state.current, nextState)) {
|
|
73
|
+
state.current = nextState;
|
|
74
|
+
forceUpdate();
|
|
75
|
+
}
|
|
76
|
+
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
77
|
+
|
|
78
|
+
return [state.current, updateState];
|
|
79
|
+
}
|
package/lib/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { default as useDerivedState } from './hooks/useDerivedState';
|
|
2
1
|
export { default as useEventListener } from './hooks/useEventListener';
|
|
3
2
|
export { default as useForceUpdate } from './hooks/useForceUpdate';
|
|
3
|
+
export { default as useReducerWithDeps } from './hooks/useReducerWithDeps';
|
|
4
|
+
export { default as useStateWithDeps } from './hooks/useStateWithDeps';
|
package/lib/utils/index.ts
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aweebit/react-essentials",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": "github:aweebit/react-essentials",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -19,9 +19,11 @@
|
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"react": ">=18.0.0 <20"
|
|
21
21
|
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@types/react": "19.1.6"
|
|
24
|
+
},
|
|
22
25
|
"devDependencies": {
|
|
23
26
|
"@eslint/js": "^9.28.0",
|
|
24
|
-
"@types/react": "^19.1.6",
|
|
25
27
|
"eslint": "^9.28.0",
|
|
26
28
|
"eslint-plugin-react-hooks": "^5.2.0",
|
|
27
29
|
"husky": "^9.1.7",
|
|
@@ -29,7 +31,7 @@
|
|
|
29
31
|
"prettier": "3.5.3",
|
|
30
32
|
"rimraf": "^6.0.1",
|
|
31
33
|
"typescript": "~5.8.3",
|
|
32
|
-
"typescript-eslint": "^8.33.
|
|
34
|
+
"typescript-eslint": "^8.33.1"
|
|
33
35
|
},
|
|
34
36
|
"license": "MIT"
|
|
35
37
|
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
|
|
3
|
-
*
|
|
4
|
-
* @license MIT
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2020 Peter Juras
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
25
|
-
*/
|
|
26
|
-
import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
|
|
27
|
-
/**
|
|
28
|
-
* `useState` hook with an additional dependency array that resets
|
|
29
|
-
* the state to the `initialState` param when the dependencies passed
|
|
30
|
-
* in the `deps` array change.
|
|
31
|
-
*
|
|
32
|
-
* @param initialState
|
|
33
|
-
* The state that will be set when the component mounts or the
|
|
34
|
-
* dependencies change.
|
|
35
|
-
*
|
|
36
|
-
* It can also be a function which resolves to the state. If the state
|
|
37
|
-
* is reset due to a change of dependencies, this function will be called with the previous
|
|
38
|
-
* state (`undefined` for the first call upon mount).
|
|
39
|
-
* @param deps Dependencies for this hook that resets the state to `initialState`
|
|
40
|
-
*/
|
|
41
|
-
export default function useDerivedState<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
|
|
42
|
-
//# sourceMappingURL=useDerivedState.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"useDerivedState.d.ts","sourceRoot":"","sources":["../../lib/hooks/useDerivedState.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAIf;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,CAAC,EACvC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,EAC5C,IAAI,EAAE,cAAc,GACnB,CAAC,CAAC,EAAE,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAsDlC"}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
|
|
3
|
-
*
|
|
4
|
-
* @license MIT
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2020 Peter Juras
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
25
|
-
*/
|
|
26
|
-
import { useCallback, useRef, } from 'react';
|
|
27
|
-
import { depsAreEqual, isFunction } from '../utils';
|
|
28
|
-
import useForceUpdate from './useForceUpdate';
|
|
29
|
-
/**
|
|
30
|
-
* `useState` hook with an additional dependency array that resets
|
|
31
|
-
* the state to the `initialState` param when the dependencies passed
|
|
32
|
-
* in the `deps` array change.
|
|
33
|
-
*
|
|
34
|
-
* @param initialState
|
|
35
|
-
* The state that will be set when the component mounts or the
|
|
36
|
-
* dependencies change.
|
|
37
|
-
*
|
|
38
|
-
* It can also be a function which resolves to the state. If the state
|
|
39
|
-
* is reset due to a change of dependencies, this function will be called with the previous
|
|
40
|
-
* state (`undefined` for the first call upon mount).
|
|
41
|
-
* @param deps Dependencies for this hook that resets the state to `initialState`
|
|
42
|
-
*/
|
|
43
|
-
export default function useDerivedState(initialState, deps) {
|
|
44
|
-
const isMounted = useRef(false);
|
|
45
|
-
// Determine initial state
|
|
46
|
-
let usableInitialState = null;
|
|
47
|
-
if (!isMounted.current) {
|
|
48
|
-
isMounted.current = true;
|
|
49
|
-
if (isFunction(initialState)) {
|
|
50
|
-
usableInitialState = initialState();
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
usableInitialState = initialState;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
// It would be possible to use useState instead of
|
|
57
|
-
// useRef to store the state, however this would
|
|
58
|
-
// trigger re-renders whenever the state is reset due
|
|
59
|
-
// to a change in dependencies. In order to avoid these
|
|
60
|
-
// re-renders, the state is stored in a ref and an
|
|
61
|
-
// update is triggered via forceUpdate below when necessary
|
|
62
|
-
const state = useRef(usableInitialState);
|
|
63
|
-
// Check if dependencies have changed
|
|
64
|
-
const prevDeps = useRef(deps);
|
|
65
|
-
if (!depsAreEqual(prevDeps.current, deps)) {
|
|
66
|
-
// Update state and deps
|
|
67
|
-
let nextState;
|
|
68
|
-
if (isFunction(initialState)) {
|
|
69
|
-
nextState = initialState(state.current);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
nextState = initialState;
|
|
73
|
-
}
|
|
74
|
-
state.current = nextState;
|
|
75
|
-
prevDeps.current = deps;
|
|
76
|
-
}
|
|
77
|
-
const [forceUpdate] = useForceUpdate();
|
|
78
|
-
const updateState = useCallback(function updateState(newState) {
|
|
79
|
-
let nextState;
|
|
80
|
-
if (isFunction(newState)) {
|
|
81
|
-
nextState = newState(state.current);
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
nextState = newState;
|
|
85
|
-
}
|
|
86
|
-
if (!Object.is(state.current, nextState)) {
|
|
87
|
-
state.current = nextState;
|
|
88
|
-
forceUpdate();
|
|
89
|
-
}
|
|
90
|
-
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
91
|
-
return [state.current, updateState];
|
|
92
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
|
|
3
|
-
*
|
|
4
|
-
* @license MIT
|
|
5
|
-
*
|
|
6
|
-
* Copyright (c) 2020 Peter Juras
|
|
7
|
-
*
|
|
8
|
-
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
9
|
-
* of this software and associated documentation files (the "Software"), to deal
|
|
10
|
-
* in the Software without restriction, including without limitation the rights
|
|
11
|
-
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
12
|
-
* copies of the Software, and to permit persons to whom the Software is
|
|
13
|
-
* furnished to do so, subject to the following conditions:
|
|
14
|
-
*
|
|
15
|
-
* The above copyright notice and this permission notice shall be included in all
|
|
16
|
-
* copies or substantial portions of the Software.
|
|
17
|
-
*
|
|
18
|
-
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
-
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
-
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
-
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
-
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
-
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
-
* SOFTWARE.
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
useCallback,
|
|
29
|
-
useRef,
|
|
30
|
-
type DependencyList,
|
|
31
|
-
type Dispatch,
|
|
32
|
-
type SetStateAction,
|
|
33
|
-
} from 'react';
|
|
34
|
-
import { depsAreEqual, isFunction } from '../utils';
|
|
35
|
-
import useForceUpdate from './useForceUpdate';
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* `useState` hook with an additional dependency array that resets
|
|
39
|
-
* the state to the `initialState` param when the dependencies passed
|
|
40
|
-
* in the `deps` array change.
|
|
41
|
-
*
|
|
42
|
-
* @param initialState
|
|
43
|
-
* The state that will be set when the component mounts or the
|
|
44
|
-
* dependencies change.
|
|
45
|
-
*
|
|
46
|
-
* It can also be a function which resolves to the state. If the state
|
|
47
|
-
* is reset due to a change of dependencies, this function will be called with the previous
|
|
48
|
-
* state (`undefined` for the first call upon mount).
|
|
49
|
-
* @param deps Dependencies for this hook that resets the state to `initialState`
|
|
50
|
-
*/
|
|
51
|
-
export default function useDerivedState<S>(
|
|
52
|
-
initialState: S | ((previousState?: S) => S),
|
|
53
|
-
deps: DependencyList,
|
|
54
|
-
): [S, Dispatch<SetStateAction<S>>] {
|
|
55
|
-
const isMounted = useRef(false);
|
|
56
|
-
|
|
57
|
-
// Determine initial state
|
|
58
|
-
let usableInitialState: S | null = null;
|
|
59
|
-
if (!isMounted.current) {
|
|
60
|
-
isMounted.current = true;
|
|
61
|
-
if (isFunction(initialState)) {
|
|
62
|
-
usableInitialState = initialState();
|
|
63
|
-
} else {
|
|
64
|
-
usableInitialState = initialState;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// It would be possible to use useState instead of
|
|
69
|
-
// useRef to store the state, however this would
|
|
70
|
-
// trigger re-renders whenever the state is reset due
|
|
71
|
-
// to a change in dependencies. In order to avoid these
|
|
72
|
-
// re-renders, the state is stored in a ref and an
|
|
73
|
-
// update is triggered via forceUpdate below when necessary
|
|
74
|
-
const state = useRef(usableInitialState as S);
|
|
75
|
-
|
|
76
|
-
// Check if dependencies have changed
|
|
77
|
-
const prevDeps = useRef(deps);
|
|
78
|
-
if (!depsAreEqual(prevDeps.current, deps)) {
|
|
79
|
-
// Update state and deps
|
|
80
|
-
let nextState: S;
|
|
81
|
-
if (isFunction(initialState)) {
|
|
82
|
-
nextState = initialState(state.current);
|
|
83
|
-
} else {
|
|
84
|
-
nextState = initialState;
|
|
85
|
-
}
|
|
86
|
-
state.current = nextState;
|
|
87
|
-
prevDeps.current = deps;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const [forceUpdate] = useForceUpdate();
|
|
91
|
-
|
|
92
|
-
const updateState = useCallback(function updateState(
|
|
93
|
-
newState: S | ((previousState: S) => S),
|
|
94
|
-
): void {
|
|
95
|
-
let nextState: S;
|
|
96
|
-
if (isFunction(newState)) {
|
|
97
|
-
nextState = newState(state.current);
|
|
98
|
-
} else {
|
|
99
|
-
nextState = newState;
|
|
100
|
-
}
|
|
101
|
-
if (!Object.is(state.current, nextState)) {
|
|
102
|
-
state.current = nextState;
|
|
103
|
-
forceUpdate();
|
|
104
|
-
}
|
|
105
|
-
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
106
|
-
|
|
107
|
-
return [state.current, updateState];
|
|
108
|
-
}
|