@aweebit/react-essentials 0.5.4 → 0.6.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.
Files changed (57) hide show
  1. package/README.md +290 -0
  2. package/dist/hooks/index.d.ts +5 -0
  3. package/dist/hooks/index.d.ts.map +1 -0
  4. package/dist/hooks/index.js +5 -0
  5. package/dist/hooks/index.js.map +1 -0
  6. package/dist/hooks/useEventListener.d.ts +69 -10
  7. package/dist/hooks/useEventListener.d.ts.map +1 -1
  8. package/dist/hooks/useEventListener.js +12 -13
  9. package/dist/hooks/useEventListener.js.map +1 -1
  10. package/dist/hooks/useForceUpdate.d.ts +3 -4
  11. package/dist/hooks/useForceUpdate.d.ts.map +1 -1
  12. package/dist/hooks/useForceUpdate.js +4 -4
  13. package/dist/hooks/useForceUpdate.js.map +1 -1
  14. package/dist/hooks/useReducerWithDeps.d.ts +13 -10
  15. package/dist/hooks/useReducerWithDeps.d.ts.map +1 -1
  16. package/dist/hooks/useReducerWithDeps.js +10 -11
  17. package/dist/hooks/useReducerWithDeps.js.map +1 -1
  18. package/dist/hooks/useStateWithDeps.d.ts +6 -7
  19. package/dist/hooks/useStateWithDeps.d.ts.map +1 -1
  20. package/dist/hooks/useStateWithDeps.js +8 -9
  21. package/dist/hooks/useStateWithDeps.js.map +1 -1
  22. package/dist/index.d.ts +2 -5
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2 -5
  25. package/dist/index.js.map +1 -1
  26. package/dist/misc/createSafeContext.d.ts +52 -0
  27. package/dist/misc/createSafeContext.d.ts.map +1 -0
  28. package/dist/misc/createSafeContext.js +46 -0
  29. package/dist/misc/createSafeContext.js.map +1 -0
  30. package/dist/misc/index.d.ts +2 -0
  31. package/dist/misc/index.d.ts.map +1 -0
  32. package/dist/misc/index.js +2 -0
  33. package/dist/misc/index.js.map +1 -0
  34. package/dist/utils.d.ts +7 -0
  35. package/dist/utils.d.ts.map +1 -0
  36. package/dist/{utils/index.js → utils.js} +1 -2
  37. package/dist/utils.js.map +1 -0
  38. package/package.json +25 -15
  39. package/src/hooks/index.ts +4 -0
  40. package/src/hooks/useEventListener.ts +170 -0
  41. package/{lib → src}/hooks/useForceUpdate.ts +8 -6
  42. package/{lib → src}/hooks/useReducerWithDeps.ts +23 -17
  43. package/{lib → src}/hooks/useStateWithDeps.ts +8 -9
  44. package/src/index.ts +2 -0
  45. package/src/misc/createSafeContext.ts +83 -0
  46. package/src/misc/index.ts +1 -0
  47. package/{lib/utils/index.ts → src/utils.ts} +9 -2
  48. package/dist/hooks/useIsomorphicLayoutEffect.d.ts +0 -4
  49. package/dist/hooks/useIsomorphicLayoutEffect.d.ts.map +0 -1
  50. package/dist/hooks/useIsomorphicLayoutEffect.js +0 -5
  51. package/dist/hooks/useIsomorphicLayoutEffect.js.map +0 -1
  52. package/dist/utils/index.d.ts +0 -5
  53. package/dist/utils/index.d.ts.map +0 -1
  54. package/dist/utils/index.js.map +0 -1
  55. package/lib/hooks/useEventListener.ts +0 -101
  56. package/lib/hooks/useIsomorphicLayoutEffect.ts +0 -7
  57. package/lib/index.ts +0 -5
@@ -6,18 +6,17 @@
6
6
  */
7
7
  import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
8
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
9
+ * `useState` hook with an additional dependency array `deps` that resets the
10
+ * state to `initialState` when dependencies change
12
11
  *
13
- * @param initialState The state that will be set when the component mounts or
14
- * the dependencies change
12
+ * @param initialState The value to which the state is set when the component is
13
+ * mounted or dependencies change
15
14
  *
16
- * It can also be a function which returns a state value. If the state is reset
15
+ * It can also be a function that returns a state value. If the state is reset
17
16
  * due to a change of dependencies, this function will be passed the previous
18
17
  * state as its argument (will be `undefined` in the first call upon mount).
19
18
  *
20
19
  * @param deps Dependencies that reset the state to `initialState`
21
20
  */
22
- export default function useStateWithDeps<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
21
+ export declare function useStateWithDeps<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
23
22
  //# sourceMappingURL=useStateWithDeps.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAIf;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,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"}
@@ -5,23 +5,22 @@
5
5
  * @copyright 2020 Peter Juras
6
6
  */
7
7
  import { useCallback, useRef, } from 'react';
8
- import { depsAreEqual, isFunction } from '../utils/index.js';
9
- import useForceUpdate from './useForceUpdate.js';
8
+ import { depsAreEqual, isFunction } from '../utils.js';
9
+ import { useForceUpdate } from './useForceUpdate.js';
10
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
11
+ * `useState` hook with an additional dependency array `deps` that resets the
12
+ * state to `initialState` when dependencies change
14
13
  *
15
- * @param initialState The state that will be set when the component mounts or
16
- * the dependencies change
14
+ * @param initialState The value to which the state is set when the component is
15
+ * mounted or dependencies change
17
16
  *
18
- * It can also be a function which returns a state value. If the state is reset
17
+ * It can also be a function that returns a state value. If the state is reset
19
18
  * due to a change of dependencies, this function will be passed the previous
20
19
  * state as its argument (will be `undefined` in the first call upon mount).
21
20
  *
22
21
  * @param deps Dependencies that reset the state to `initialState`
23
22
  */
24
- export default function useStateWithDeps(initialState, deps) {
23
+ export function useStateWithDeps(initialState, deps) {
25
24
  // It would be possible to use useState instead of
26
25
  // useRef to store the state, however this would
27
26
  // trigger re-renders whenever the state is reset due
@@ -1 +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,mBAAmB,CAAC;AAC7D,OAAO,cAAc,MAAM,qBAAqB,CAAC;AAEjD;;;;;;;;;;;;;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"}
1
+ {"version":3,"file":"useStateWithDeps.js","sourceRoot":"","sources":["../../src/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,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,gBAAgB,CAC9B,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,6 +1,3 @@
1
- export { default as useEventListener } from './hooks/useEventListener.js';
2
- export { default as useForceUpdate } from './hooks/useForceUpdate.js';
3
- export { default as useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect.js';
4
- export { default as useReducerWithDeps } from './hooks/useReducerWithDeps.js';
5
- export { default as useStateWithDeps } from './hooks/useStateWithDeps.js';
1
+ export * from './hooks/index.js';
2
+ export * from './misc/index.js';
6
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
package/dist/index.js CHANGED
@@ -1,6 +1,3 @@
1
- export { default as useEventListener } from './hooks/useEventListener.js';
2
- export { default as useForceUpdate } from './hooks/useForceUpdate.js';
3
- export { default as useIsomorphicLayoutEffect } from './hooks/useIsomorphicLayoutEffect.js';
4
- export { default as useReducerWithDeps } from './hooks/useReducerWithDeps.js';
5
- export { default as useStateWithDeps } from './hooks/useStateWithDeps.js';
1
+ export * from './hooks/index.js';
2
+ export * from './misc/index.js';
6
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,sCAAsC,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
@@ -0,0 +1,52 @@
1
+ import { type Context, type Provider } from 'react';
2
+ import type { ArgumentFallback } from '../utils.js';
3
+ /**
4
+ * A React context with a required `displayName` and the obsolete `Consumer`
5
+ * property purposefully omitted so that it is impossible to pass the context
6
+ * as an argument to `useContext` or `use` (the hook produced with
7
+ * {@linkcode createSafeContext} should be used instead)
8
+ *
9
+ * @see {@linkcode createSafeContext}
10
+ */
11
+ export type RestrictedContext<T> = Context<T> extends Provider<T> ? {
12
+ Provider: Provider<T>;
13
+ displayName: string;
14
+ } & Provider<T> : {
15
+ Provider: Provider<T>;
16
+ displayName: string;
17
+ };
18
+ /**
19
+ * @see {@linkcode createSafeContext}
20
+ */
21
+ export type SafeContext<DisplayName extends string, T> = {
22
+ [K in `${DisplayName}Context`]: RestrictedContext<T>;
23
+ } & {
24
+ [K in `use${DisplayName}`]: () => T;
25
+ };
26
+ /**
27
+ * For a given type `T`, returns a function that produces both a context of that
28
+ * type and a hook that returns the current context value if one was provided,
29
+ * or throws an error otherwise
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * const { ItemsContext, useItems } = createSafeContext<string[]>()('Items');
34
+ *
35
+ * const Parent = () => (
36
+ * <ItemsContext value={['compass', 'newspaper', 'banana']}>
37
+ * <Child />
38
+ * </ItemsContext>
39
+ * );
40
+ *
41
+ * const Child = () => useItems().join(', ');
42
+ * ```
43
+ *
44
+ * @returns
45
+ * A function that accepts a single string argument `displayName` (e.g.
46
+ * `"Items"`) and returns an object with the following properties:
47
+ * - ``` `${displayName}Context` ``` (e.g. `ItemsContext`): the context
48
+ * - ``` `use${displayName}` ``` (e.g. `useItems`): a hook that returns the
49
+ * current context value if one was provided, or throws an error otherwise
50
+ */
51
+ export declare function createSafeContext<T = never>(): <DisplayName extends string>(displayName: [T] extends [never] ? never : ArgumentFallback<DisplayName, never, string>) => SafeContext<DisplayName, T>;
52
+ //# sourceMappingURL=createSafeContext.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createSafeContext.d.ts","sourceRoot":"","sources":["../../src/misc/createSafeContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ,EAA6B,MAAM,OAAO,CAAC;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAIpD;;;;;;;GAOG;AAIH,MAAM,MAAM,iBAAiB,CAAC,CAAC,IAC7B,OAAO,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,CAAC,GAC1B;IAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,QAAQ,CAAC,CAAC,CAAC,GAC5D;IAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAErD;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,WAAW,SAAS,MAAM,EAAE,CAAC,IAAI;KACtD,CAAC,IAAI,GAAG,WAAW,SAAS,GAAG,iBAAiB,CAAC,CAAC,CAAC;CACrD,GAAG;KACD,CAAC,IAAI,MAAM,WAAW,EAAE,GAAG,MAAM,CAAC;CACpC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,GAAG,KAAK,MACjC,WAAW,SAAS,MAAM,EAChC,aAAa,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAC5B,KAAK,GACL,gBAAgB,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,CAAC,KAC/C,WAAW,CAAC,WAAW,EAAE,CAAC,CAAC,CAsB/B"}
@@ -0,0 +1,46 @@
1
+ import { createContext, useContext } from 'react';
2
+ const moValueSymbol = Symbol('noValue');
3
+ /**
4
+ * For a given type `T`, returns a function that produces both a context of that
5
+ * type and a hook that returns the current context value if one was provided,
6
+ * or throws an error otherwise
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * const { ItemsContext, useItems } = createSafeContext<string[]>()('Items');
11
+ *
12
+ * const Parent = () => (
13
+ * <ItemsContext value={['compass', 'newspaper', 'banana']}>
14
+ * <Child />
15
+ * </ItemsContext>
16
+ * );
17
+ *
18
+ * const Child = () => useItems().join(', ');
19
+ * ```
20
+ *
21
+ * @returns
22
+ * A function that accepts a single string argument `displayName` (e.g.
23
+ * `"Items"`) and returns an object with the following properties:
24
+ * - ``` `${displayName}Context` ``` (e.g. `ItemsContext`): the context
25
+ * - ``` `use${displayName}` ``` (e.g. `useItems`): a hook that returns the
26
+ * current context value if one was provided, or throws an error otherwise
27
+ */
28
+ export function createSafeContext() {
29
+ return (displayName) => {
30
+ const contextName = `${displayName}Context`;
31
+ const hookName = `use${displayName}`;
32
+ const Context = createContext(moValueSymbol);
33
+ Context.displayName = contextName;
34
+ return {
35
+ [contextName]: Context,
36
+ [hookName]: () => {
37
+ const value = useContext(Context);
38
+ if (value === moValueSymbol) {
39
+ throw new Error(`No ${contextName} value was provided`);
40
+ }
41
+ return value;
42
+ },
43
+ };
44
+ };
45
+ }
46
+ //# sourceMappingURL=createSafeContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createSafeContext.js","sourceRoot":"","sources":["../../src/misc/createSafeContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAG/E,MAAM,aAAa,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;AA2BxC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,iBAAiB;IAC/B,OAAO,CACL,WAEgD,EACnB,EAAE;QAC/B,MAAM,WAAW,GAAG,GAAG,WAA0B,SAAkB,CAAC;QACpE,MAAM,QAAQ,GAAG,MAAM,WAA0B,EAAW,CAAC;QAE7D,MAAM,OAAO,GAAG,aAAa,CAA2B,aAAa,CAAC,CAAC;QACvE,OAAO,CAAC,WAAW,GAAG,WAAW,CAAC;QAElC,OAAO;YACL,CAAC,WAAW,CAAC,EAAE,OAA+B;YAC9C,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE;gBACf,MAAM,KAAK,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;gBAClC,IAAI,KAAK,KAAK,aAAa,EAAE,CAAC;oBAC5B,MAAM,IAAI,KAAK,CAAC,MAAM,WAAW,qBAAqB,CAAC,CAAC;gBAC1D,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC;SAKF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './createSafeContext.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/misc/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from './createSafeContext.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/misc/index.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import type { DependencyList } from 'react';
2
+ export type Callable = (...args: never) => unknown;
3
+ export type ArgumentFallback<T extends Base, Default extends Base, Base = unknown> = [T] extends [never] ? Default : [Base] extends [T] ? Default : T;
4
+ export declare function noop(): void;
5
+ export declare function isFunction(input: unknown): input is Callable;
6
+ export declare function depsAreEqual(prevDeps: DependencyList, deps: DependencyList): boolean;
7
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,MAAM,QAAQ,GAAG,CAAC,GAAG,IAAI,EAAE,KAAK,KAAK,OAAO,CAAC;AAEnD,MAAM,MAAM,gBAAgB,CAC1B,CAAC,SAAS,IAAI,EACd,OAAO,SAAS,IAAI,EACpB,IAAI,GAAG,OAAO,IACZ,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC,CAAC;AAErE,wBAAgB,IAAI,SAAK;AAEzB,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,QAAQ,CAE5D;AAED,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,cAAc,GACnB,OAAO,CAKT"}
@@ -1,5 +1,4 @@
1
1
  export function noop() { }
2
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
3
2
  export function isFunction(input) {
4
3
  return typeof input === 'function';
5
4
  }
@@ -7,4 +6,4 @@ export function depsAreEqual(prevDeps, deps) {
7
6
  return (prevDeps.length === deps.length &&
8
7
  deps.every((dep, index) => Object.is(dep, prevDeps[index])));
9
8
  }
10
- //# sourceMappingURL=index.js.map
9
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,IAAI,KAAI,CAAC;AAEzB,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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aweebit/react-essentials",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "repository": "github:aweebit/react-essentials",
6
6
  "main": "dist/index.js",
@@ -10,28 +10,38 @@
10
10
  "prettier": "npm run prettier:base -- .",
11
11
  "lint": "eslint --max-warnings=0",
12
12
  "build": "rimraf dist && tsc -b -f",
13
- "prepare": "husky && npm run build"
14
- },
15
- "lint-staged": {
16
- "!(*.{jsx,jsx,ts,tsx})": "npm run prettier -- --ignore-unknown",
17
- "**.{jsx,jsx,ts,tsx}": "npm run lint --"
13
+ "doc": "rimraf README.md && typedoc",
14
+ "prepack": "npm run build && npm run doc",
15
+ "prepare": "husky"
18
16
  },
19
17
  "peerDependencies": {
20
- "react": ">=18.0.0 <20"
18
+ "@types/react": ">=18",
19
+ "react": ">=18",
20
+ "typescript": ">=5.4"
21
21
  },
22
- "dependencies": {
23
- "@types/react": "19.1.6"
22
+ "peerDependenciesMeta": {
23
+ "@types/react": {
24
+ "optional": true
25
+ },
26
+ "typescript": {
27
+ "optional": true
28
+ }
24
29
  },
25
30
  "devDependencies": {
26
- "@eslint/js": "^9.28.0",
27
- "eslint": "^9.28.0",
31
+ "@eslint/js": "^9.36.0",
32
+ "@types/react": "^18.3.24",
33
+ "eslint": "^9.36.0",
34
+ "eslint-config-prettier": "^10.1.8",
28
35
  "eslint-plugin-react-hooks": "^5.2.0",
29
36
  "husky": "^9.1.7",
30
- "lint-staged": "^16.1.0",
31
- "prettier": "3.5.3",
37
+ "lint-staged": "^16.1.6",
38
+ "prettier": "3.6.2",
39
+ "react": "^18.3.1",
32
40
  "rimraf": "^6.0.1",
33
- "typescript": "~5.8.3",
34
- "typescript-eslint": "^8.33.1"
41
+ "typedoc": "^0.28.13",
42
+ "typedoc-plugin-markdown": "^4.9.0",
43
+ "typescript": "~5.9.2",
44
+ "typescript-eslint": "^8.44.0"
35
45
  },
36
46
  "license": "MIT"
37
47
  }
@@ -0,0 +1,4 @@
1
+ export * from './useEventListener.js';
2
+ export * from './useForceUpdate.js';
3
+ export * from './useReducerWithDeps.js';
4
+ export * from './useStateWithDeps.js';
@@ -0,0 +1,170 @@
1
+ /**
2
+ * @file Based on {@link https://github.com/juliencrn/usehooks-ts}
3
+ *
4
+ * @license MIT
5
+ * @copyright 2020 Julien CARON
6
+ */
7
+
8
+ import { useEffect, useMemo, useRef } from 'react';
9
+
10
+ /**
11
+ * Adds `handler` as a listener for the event `eventName` of `element` with the
12
+ * provided `options` applied
13
+ *
14
+ * If `element` is `undefined`, `window` is used instead.
15
+ *
16
+ * If `element` is `null`, no event listener is added.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * useEventListener('resize', () => {
21
+ * console.log(window.innerWidth, window.innerHeight);
22
+ * });
23
+ *
24
+ * useEventListener(
25
+ * 'visibilitychange',
26
+ * () => console.log(document.visibilityState),
27
+ * document
28
+ * );
29
+ *
30
+ * const buttonRef = useRef<HTMLButtonElement>(null);
31
+ * useEventListener("click", () => console.log("click"), buttonRef.current);
32
+ * ```
33
+ *
34
+ * @ignore
35
+ */
36
+ export function useEventListener<
37
+ K extends keyof HTMLElementEventMap,
38
+ T extends HTMLElement,
39
+ >(
40
+ eventName: K,
41
+ handler: (this: NoInfer<T>, event: HTMLElementEventMap[K]) => void,
42
+ element: T | null,
43
+ options?: boolean | AddEventListenerOptions,
44
+ ): void;
45
+
46
+ /**
47
+ * @see {@linkcode useEventListener}
48
+ * @ignore
49
+ */
50
+ export function useEventListener<
51
+ K extends keyof SVGElementEventMap,
52
+ T extends SVGElement,
53
+ >(
54
+ eventName: K,
55
+ handler: (this: NoInfer<T>, event: SVGElementEventMap[K]) => void,
56
+ element: T | null,
57
+ options?: boolean | AddEventListenerOptions,
58
+ ): void;
59
+
60
+ /**
61
+ * @see {@linkcode useEventListener}
62
+ * @ignore
63
+ */
64
+ export function useEventListener<
65
+ K extends keyof MathMLElementEventMap,
66
+ T extends MathMLElement,
67
+ >(
68
+ eventName: K,
69
+ handler: (this: NoInfer<T>, event: MathMLElementEventMap[K]) => void,
70
+ element: T | null,
71
+ options?: boolean | AddEventListenerOptions,
72
+ ): void;
73
+
74
+ /**
75
+ * @see {@linkcode useEventListener}
76
+ * @ignore
77
+ */
78
+ export function useEventListener<K extends keyof DocumentEventMap>(
79
+ eventName: K,
80
+ handler: (this: Document, event: DocumentEventMap[K]) => void,
81
+ element: Document,
82
+ options?: boolean | AddEventListenerOptions,
83
+ ): void;
84
+
85
+ /**
86
+ * @see {@linkcode useEventListener}
87
+ * @ignore
88
+ */
89
+ export function useEventListener<K extends keyof WindowEventMap>(
90
+ eventName: K,
91
+ handler: (this: Window, event: WindowEventMap[K]) => void,
92
+ element?: Window,
93
+ options?: boolean | AddEventListenerOptions,
94
+ ): void;
95
+
96
+ /**
97
+ * Adds `handler` as a listener for the event `eventName` of `element` with the
98
+ * provided `options` applied
99
+ *
100
+ * If `element` is `undefined`, `window` is used instead.
101
+ *
102
+ * If `element` is `null`, no event listener is added.
103
+ *
104
+ * @example
105
+ * ```tsx
106
+ * useEventListener('resize', () => {
107
+ * console.log(window.innerWidth, window.innerHeight);
108
+ * });
109
+ *
110
+ * useEventListener(
111
+ * 'visibilitychange',
112
+ * () => console.log(document.visibilityState),
113
+ * document
114
+ * );
115
+ *
116
+ * const buttonRef = useRef<HTMLButtonElement>(null);
117
+ * useEventListener("click", () => console.log("click"), buttonRef.current);
118
+ * ```
119
+ */
120
+ export function useEventListener<T extends EventTarget>(
121
+ eventName: string,
122
+ handler: (this: NoInfer<T>, event: Event) => void,
123
+ element?: T | null,
124
+ options?: boolean | AddEventListenerOptions,
125
+ ): void;
126
+
127
+ export function useEventListener(
128
+ eventName: string,
129
+ handler: (this: EventTarget, event: Event) => void,
130
+ element?: EventTarget | null,
131
+ options?: boolean | AddEventListenerOptions,
132
+ ) {
133
+ const handlerRef = useRef(handler);
134
+ handlerRef.current = handler;
135
+
136
+ const {
137
+ capture = false,
138
+ once = false,
139
+ passive,
140
+ signal,
141
+ } = typeof options === 'boolean' ? { capture: options } : (options ?? {});
142
+
143
+ const memoizedOptions = useMemo(
144
+ () => options,
145
+ // eslint-disable-next-line react-hooks/exhaustive-deps
146
+ [capture, once, passive, signal],
147
+ );
148
+
149
+ useEffect(() => {
150
+ if (element === null) {
151
+ // No element has been attached to the ref yet
152
+ return;
153
+ }
154
+
155
+ // Define the listening target
156
+ const targetElement = element ?? window;
157
+
158
+ // Create event listener that calls handler function stored in ref
159
+ const listener: typeof handler = function (event) {
160
+ handlerRef.current.call(this, event);
161
+ };
162
+
163
+ targetElement.addEventListener(eventName, listener, memoizedOptions);
164
+
165
+ // Remove event listener on cleanup
166
+ return () => {
167
+ targetElement.removeEventListener(eventName, listener, memoizedOptions);
168
+ };
169
+ }, [eventName, element, memoizedOptions]);
170
+ }
@@ -1,5 +1,10 @@
1
1
  import { useReducer, useRef } from 'react';
2
2
 
3
+ /* eslint-disable */
4
+ import type { useStateWithDeps } from './useStateWithDeps.js';
5
+ import type { useReducerWithDeps } from './useReducerWithDeps.js';
6
+ /* eslint-enable */
7
+
3
8
  /**
4
9
  * Enables you to imperatively trigger re-rendering of components
5
10
  *
@@ -12,9 +17,8 @@ import { useReducer, useRef } from 'react';
12
17
  * Can be used for conditionally calling state setters when state needs to be
13
18
  * reset. That is legal and better than using effects (see
14
19
  * {@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}).
20
+ * but can often be avoided by using {@linkcode useStateWithDeps} or
21
+ * {@linkcode useReducerWithDeps}.
18
22
  *
19
23
  * Important: the callback function is called once per render, not once per
20
24
  * `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
@@ -25,9 +29,7 @@ import { useReducer, useRef } from 'react';
25
29
  * 1. A `forceUpdate` function that triggers a re-render
26
30
  * 2. The number of times `forceUpdate` has been called so far
27
31
  */
28
- export default function useForceUpdate(
29
- callback?: () => void,
30
- ): [() => void, bigint] {
32
+ export function useForceUpdate(callback?: () => void): [() => void, bigint] {
31
33
  // It is very unlikely that the number of updates will exceed
32
34
  // Number.MAX_SAFE_INTEGER, but not impossible. That is why we use bigints.
33
35
  const [counter, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
@@ -1,16 +1,22 @@
1
- import {
2
- type ActionDispatch,
3
- type AnyActionArg,
4
- type DependencyList,
5
- useCallback,
6
- useRef,
7
- } from 'react';
8
- import useStateWithDeps from './useStateWithDeps.js';
1
+ import { type DependencyList, useCallback, useRef } from 'react';
2
+ import { useStateWithDeps } from './useStateWithDeps.js';
3
+
4
+ // We cannot simply import the following types from @types/react since they are
5
+ // only available starting from React 19, but we also want to support React 18
6
+ // whose type declarations for useReducer are very different.
7
+
8
+ /** @ignore */
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ export type AnyActionArg = [] | [any];
11
+
12
+ /** @ignore */
13
+ export type ActionDispatch<ActionArg extends AnyActionArg> = (
14
+ ...args: ActionArg
15
+ ) => void;
9
16
 
10
17
  /**
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
18
+ * `useReducer` hook with an additional dependency array `deps` that resets the
19
+ * state to `initialState` when dependencies change
14
20
  *
15
21
  * ### On linter support
16
22
  *
@@ -19,7 +25,7 @@ import useStateWithDeps from './useStateWithDeps.js';
19
25
  * However, as we would like to keep the hook as compatible with `useReducer` as
20
26
  * possible, we don't want to artificially change the parameter's position.
21
27
  * Therefore, there will be no warnings about missing dependencies.
22
- * Because of that, addition caution is advised!
28
+ * Because of that, additional caution is advised!
23
29
  * Be sure to check no dependencies are missing from the `deps` array.
24
30
  *
25
31
  * Related issue: {@link https://github.com/facebook/react/issues/25443}.
@@ -27,20 +33,20 @@ import useStateWithDeps from './useStateWithDeps.js';
27
33
  * Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
28
34
  * `useExhaustiveDependencies` rule provided for Biome by Biome's team
29
35
  * does actually have support for dependency arrays at other positions, see
30
- * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
36
+ * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies useExhaustiveDependencies > Options > Validating dependencies}.
31
37
  *
32
38
  * @param reducer The reducer function that specifies how the state gets updated
33
39
  *
34
- * @param initialState The state that will be set when the component mounts or
35
- * the dependencies change
40
+ * @param initialState The value to which the state is set when the component is
41
+ * mounted or dependencies change
36
42
  *
37
- * It can also be a function which returns a state value. If the state is reset
43
+ * It can also be a function that returns a state value. If the state is reset
38
44
  * due to a change of dependencies, this function will be passed the previous
39
45
  * state as its argument (will be `undefined` in the first call upon mount).
40
46
  *
41
47
  * @param deps Dependencies that reset the state to `initialState`
42
48
  */
43
- export default function useReducerWithDeps<S, A extends AnyActionArg>(
49
+ export function useReducerWithDeps<S, A extends AnyActionArg>(
44
50
  reducer: (prevState: S, ...args: A) => S,
45
51
  initialState: S | ((previousState?: S) => S),
46
52
  deps: DependencyList,
@@ -12,24 +12,23 @@ import {
12
12
  type Dispatch,
13
13
  type SetStateAction,
14
14
  } from 'react';
15
- import { depsAreEqual, isFunction } from '../utils/index.js';
16
- import useForceUpdate from './useForceUpdate.js';
15
+ import { depsAreEqual, isFunction } from '../utils.js';
16
+ import { useForceUpdate } from './useForceUpdate.js';
17
17
 
18
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
19
+ * `useState` hook with an additional dependency array `deps` that resets the
20
+ * state to `initialState` when dependencies change
22
21
  *
23
- * @param initialState The state that will be set when the component mounts or
24
- * the dependencies change
22
+ * @param initialState The value to which the state is set when the component is
23
+ * mounted or dependencies change
25
24
  *
26
- * It can also be a function which returns a state value. If the state is reset
25
+ * It can also be a function that returns a state value. If the state is reset
27
26
  * due to a change of dependencies, this function will be passed the previous
28
27
  * state as its argument (will be `undefined` in the first call upon mount).
29
28
  *
30
29
  * @param deps Dependencies that reset the state to `initialState`
31
30
  */
32
- export default function useStateWithDeps<S>(
31
+ export function useStateWithDeps<S>(
33
32
  initialState: S | ((previousState?: S) => S),
34
33
  deps: DependencyList,
35
34
  ): [S, Dispatch<SetStateAction<S>>] {
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './hooks/index.js';
2
+ export * from './misc/index.js';