@aweebit/react-essentials 0.4.0 → 0.5.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/dist/hooks/useEventListener.d.ts +1 -1
- package/dist/hooks/useEventListener.js +1 -0
- 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/{useDerivedState.d.ts → useStateWithDeps.d.ts} +14 -14
- package/dist/hooks/useStateWithDeps.d.ts.map +1 -0
- package/dist/hooks/{useDerivedState.js → useStateWithDeps.js} +17 -26
- 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.js +1 -0
- package/dist/utils/index.js.map +1 -0
- package/lib/hooks/useEventListener.ts +1 -1
- package/lib/hooks/useForceUpdate.ts +38 -7
- package/lib/hooks/useReducerWithDeps.ts +59 -0
- package/lib/hooks/{useDerivedState.ts → useStateWithDeps.ts} +17 -27
- package/lib/index.ts +2 -1
- package/package.json +5 -3
- package/dist/hooks/useDerivedState.d.ts.map +0 -1
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
*/
|
|
26
26
|
/**
|
|
27
27
|
* Adds `handler` as a listener for the event `eventName` of `element`
|
|
28
|
-
* (or `window` by default) with the provided `options` applied
|
|
28
|
+
* (or `window` by default) with the provided `options` applied
|
|
29
29
|
*
|
|
30
30
|
* It is the user's responsibility to make sure `element` and `options` values
|
|
31
31
|
* are correctly memoized!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../lib/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;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"}
|
|
@@ -25,18 +25,18 @@
|
|
|
25
25
|
*/
|
|
26
26
|
import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
|
|
27
27
|
/**
|
|
28
|
-
* `useState` hook with an additional dependency array that resets
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* @param initialState
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* @param deps Dependencies
|
|
28
|
+
* `useState` hook with an additional dependency array that resets the state
|
|
29
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
30
|
+
* change
|
|
31
|
+
*
|
|
32
|
+
* @param initialState The state that will be set when the component mounts or
|
|
33
|
+
* the dependencies change
|
|
34
|
+
*
|
|
35
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
36
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
37
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
38
|
+
*
|
|
39
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
40
40
|
*/
|
|
41
|
-
export default function
|
|
42
|
-
//# sourceMappingURL=
|
|
41
|
+
export default function useStateWithDeps<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
|
|
42
|
+
//# sourceMappingURL=useStateWithDeps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../lib/hooks/useStateWithDeps.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,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"}
|
|
@@ -27,42 +27,31 @@ import { useCallback, useRef, } from 'react';
|
|
|
27
27
|
import { depsAreEqual, isFunction } from '../utils';
|
|
28
28
|
import useForceUpdate from './useForceUpdate';
|
|
29
29
|
/**
|
|
30
|
-
* `useState` hook with an additional dependency array that resets
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* `useState` hook with an additional dependency array that resets the state
|
|
31
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
32
|
+
* change
|
|
33
33
|
*
|
|
34
|
-
* @param initialState
|
|
35
|
-
*
|
|
36
|
-
* dependencies change.
|
|
34
|
+
* @param initialState The state that will be set when the component mounts or
|
|
35
|
+
* the dependencies change
|
|
37
36
|
*
|
|
38
|
-
* It can also be a function which
|
|
39
|
-
*
|
|
40
|
-
* state (`undefined`
|
|
41
|
-
*
|
|
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
42
|
*/
|
|
43
|
-
export default function
|
|
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
|
-
}
|
|
43
|
+
export default function useStateWithDeps(initialState, deps) {
|
|
56
44
|
// It would be possible to use useState instead of
|
|
57
45
|
// useRef to store the state, however this would
|
|
58
46
|
// trigger re-renders whenever the state is reset due
|
|
59
47
|
// to a change in dependencies. In order to avoid these
|
|
60
48
|
// re-renders, the state is stored in a ref and an
|
|
61
49
|
// update is triggered via forceUpdate below when necessary
|
|
62
|
-
const state = useRef(
|
|
63
|
-
// Check if dependencies have changed
|
|
50
|
+
const state = useRef(undefined);
|
|
64
51
|
const prevDeps = useRef(deps);
|
|
65
|
-
|
|
52
|
+
const isMounted = useRef(false);
|
|
53
|
+
// If first render, or if dependencies have changed since last time
|
|
54
|
+
if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
|
|
66
55
|
// Update state and deps
|
|
67
56
|
let nextState;
|
|
68
57
|
if (isFunction(initialState)) {
|
|
@@ -73,6 +62,7 @@ export default function useDerivedState(initialState, deps) {
|
|
|
73
62
|
}
|
|
74
63
|
state.current = nextState;
|
|
75
64
|
prevDeps.current = deps;
|
|
65
|
+
isMounted.current = true;
|
|
76
66
|
}
|
|
77
67
|
const [forceUpdate] = useForceUpdate();
|
|
78
68
|
const updateState = useCallback(function updateState(newState) {
|
|
@@ -90,3 +80,4 @@ export default function useDerivedState(initialState, deps) {
|
|
|
90
80
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
91
81
|
return [state.current, updateState];
|
|
92
82
|
}
|
|
83
|
+
//# sourceMappingURL=useStateWithDeps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStateWithDeps.js","sourceRoot":"","sources":["../../lib/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;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.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"}
|
|
@@ -28,7 +28,7 @@ import { useEffect, useLayoutEffect, useRef } from 'react';
|
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Adds `handler` as a listener for the event `eventName` of `element`
|
|
31
|
-
* (or `window` by default) with the provided `options` applied
|
|
31
|
+
* (or `window` by default) with the provided `options` applied
|
|
32
32
|
*
|
|
33
33
|
* It is the user's responsibility to make sure `element` and `options` values
|
|
34
34
|
* 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
|
+
}
|
|
@@ -35,47 +35,36 @@ import { depsAreEqual, isFunction } from '../utils';
|
|
|
35
35
|
import useForceUpdate from './useForceUpdate';
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
* `useState` hook with an additional dependency array that resets
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* `useState` hook with an additional dependency array that resets the state
|
|
39
|
+
* to the `initialState` param when the dependencies passed in the `deps` array
|
|
40
|
+
* change
|
|
41
41
|
*
|
|
42
|
-
* @param initialState
|
|
43
|
-
*
|
|
44
|
-
* dependencies change.
|
|
42
|
+
* @param initialState The state that will be set when the component mounts or
|
|
43
|
+
* the dependencies change
|
|
45
44
|
*
|
|
46
|
-
* It can also be a function which
|
|
47
|
-
*
|
|
48
|
-
* state (`undefined`
|
|
49
|
-
*
|
|
45
|
+
* It can also be a function which returns a state value. If the state is reset
|
|
46
|
+
* due to a change of dependencies, this function will be passed the previous
|
|
47
|
+
* state as its argument (will be `undefined` in the first call upon mount).
|
|
48
|
+
*
|
|
49
|
+
* @param deps Dependencies that reset the state to `initialState`
|
|
50
50
|
*/
|
|
51
|
-
export default function
|
|
51
|
+
export default function useStateWithDeps<S>(
|
|
52
52
|
initialState: S | ((previousState?: S) => S),
|
|
53
53
|
deps: DependencyList,
|
|
54
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
55
|
// It would be possible to use useState instead of
|
|
69
56
|
// useRef to store the state, however this would
|
|
70
57
|
// trigger re-renders whenever the state is reset due
|
|
71
58
|
// to a change in dependencies. In order to avoid these
|
|
72
59
|
// re-renders, the state is stored in a ref and an
|
|
73
60
|
// update is triggered via forceUpdate below when necessary
|
|
74
|
-
const state = useRef(
|
|
61
|
+
const state = useRef(undefined as S);
|
|
75
62
|
|
|
76
|
-
// Check if dependencies have changed
|
|
77
63
|
const prevDeps = useRef(deps);
|
|
78
|
-
|
|
64
|
+
const isMounted = useRef(false);
|
|
65
|
+
|
|
66
|
+
// If first render, or if dependencies have changed since last time
|
|
67
|
+
if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
|
|
79
68
|
// Update state and deps
|
|
80
69
|
let nextState: S;
|
|
81
70
|
if (isFunction(initialState)) {
|
|
@@ -85,6 +74,7 @@ export default function useDerivedState<S>(
|
|
|
85
74
|
}
|
|
86
75
|
state.current = nextState;
|
|
87
76
|
prevDeps.current = deps;
|
|
77
|
+
isMounted.current = true;
|
|
88
78
|
}
|
|
89
79
|
|
|
90
80
|
const [forceUpdate] = useForceUpdate();
|
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aweebit/react-essentials",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
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 +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"}
|