@aweebit/react-essentials 0.5.4 → 0.7.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 (59) hide show
  1. package/README.md +836 -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 +65 -11
  7. package/dist/hooks/useEventListener.d.ts.map +1 -1
  8. package/dist/hooks/useEventListener.js +49 -18
  9. package/dist/hooks/useEventListener.js.map +1 -1
  10. package/dist/hooks/useForceUpdate.d.ts +52 -4
  11. package/dist/hooks/useForceUpdate.d.ts.map +1 -1
  12. package/dist/hooks/useForceUpdate.js +58 -9
  13. package/dist/hooks/useForceUpdate.js.map +1 -1
  14. package/dist/hooks/useReducerWithDeps.d.ts +17 -11
  15. package/dist/hooks/useReducerWithDeps.d.ts.map +1 -1
  16. package/dist/hooks/useReducerWithDeps.js +14 -12
  17. package/dist/hooks/useReducerWithDeps.js.map +1 -1
  18. package/dist/hooks/useStateWithDeps.d.ts +41 -7
  19. package/dist/hooks/useStateWithDeps.d.ts.map +1 -1
  20. package/dist/hooks/useStateWithDeps.js +47 -15
  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 +85 -0
  27. package/dist/misc/createSafeContext.d.ts.map +1 -0
  28. package/dist/misc/createSafeContext.js +77 -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 +201 -0
  41. package/src/hooks/useForceUpdate.ts +91 -0
  42. package/{lib → src}/hooks/useReducerWithDeps.ts +27 -18
  43. package/src/hooks/useStateWithDeps.ts +111 -0
  44. package/src/index.ts +2 -0
  45. package/src/misc/createSafeContext.ts +116 -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/useForceUpdate.ts +0 -40
  57. package/lib/hooks/useIsomorphicLayoutEffect.ts +0 -7
  58. package/lib/hooks/useStateWithDeps.ts +0 -79
  59. package/lib/index.ts +0 -5
@@ -0,0 +1,5 @@
1
+ export * from './useEventListener.js';
2
+ export * from './useForceUpdate.js';
3
+ export * from './useReducerWithDeps.js';
4
+ export * from './useStateWithDeps.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export * from './useEventListener.js';
2
+ export * from './useForceUpdate.js';
3
+ export * from './useReducerWithDeps.js';
4
+ export * from './useStateWithDeps.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,qBAAqB,CAAC;AACpC,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
@@ -4,17 +4,71 @@
4
4
  * @license MIT
5
5
  * @copyright 2020 Julien CARON
6
6
  */
7
+ type UseEventListenerOverloadArgs<EventMap, K extends keyof EventMap, T extends EventTarget> = [
8
+ target: T | null,
9
+ eventName: K,
10
+ handler: (this: NoInfer<T>, event: EventMap[K]) => void,
11
+ options?: AddEventListenerOptions | boolean
12
+ ];
7
13
  /**
8
- * Adds `handler` as a listener for the event `eventName` of `element`
9
- * (or `window` by default) with the provided `options` applied
14
+ * Adds `handler` as a listener for the event `eventName` of `target` with the
15
+ * provided `options` applied
10
16
  *
11
- * It is the user's responsibility to make sure `element` and `options` values
12
- * are correctly memoized!
13
- */
14
- declare function useEventListener<K extends keyof SVGElementEventMap, T extends SVGElement>(eventName: K, handler: (this: T, event: SVGElementEventMap[K]) => void, element: T | null, options?: boolean | AddEventListenerOptions): void;
15
- declare function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement>(eventName: K, handler: (this: T, event: HTMLElementEventMap[K]) => void, element: T | null, options?: boolean | AddEventListenerOptions): void;
16
- declare function useEventListener<K extends keyof DocumentEventMap>(eventName: K, handler: (this: Document, event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
17
- declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (this: Window, event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
18
- declare function useEventListener<T extends EventTarget>(eventName: string, handler: (this: T, event: Event) => void, element?: T | null, options?: boolean | AddEventListenerOptions): void;
19
- export default useEventListener;
17
+ * If `target` is not provided, `window` is used instead.
18
+ *
19
+ * If `target` is `null`, no event listener is added. This is useful when
20
+ * working with DOM element refs, or when the event listener needs to be removed
21
+ * temporarily.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * useEventListener('resize', () => {
26
+ * console.log(window.innerWidth, window.innerHeight);
27
+ * });
28
+ *
29
+ * useEventListener(document, 'visibilitychange', () => {
30
+ * console.log(document.visibilityState);
31
+ * });
32
+ *
33
+ * const buttonRef = useRef<HTMLButtonElement>(null);
34
+ * useEventListener(buttonRef.current, 'click', () => console.log('click'));
35
+ * ```
36
+ *
37
+ * @ignore
38
+ */
39
+ export declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (this: Window, event: WindowEventMap[K]) => void, options?: AddEventListenerOptions | boolean): void;
40
+ /**
41
+ * @see {@linkcode useEventListener}
42
+ * @ignore
43
+ */
44
+ export declare function useEventListener<K extends keyof WindowEventMap>(...args: UseEventListenerOverloadArgs<WindowEventMap, K, Window>): void;
45
+ /**
46
+ * @see {@linkcode useEventListener}
47
+ * @ignore
48
+ */
49
+ export declare function useEventListener<K extends keyof DocumentEventMap>(...args: UseEventListenerOverloadArgs<DocumentEventMap, K, Document>): void;
50
+ /**
51
+ * @see {@linkcode useEventListener}
52
+ * @ignore
53
+ */
54
+ export declare function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement>(...args: UseEventListenerOverloadArgs<HTMLElementEventMap, K, T>): void;
55
+ /**
56
+ * @see {@linkcode useEventListener}
57
+ * @ignore
58
+ */
59
+ export declare function useEventListener<K extends keyof SVGElementEventMap, T extends SVGElement>(...args: UseEventListenerOverloadArgs<SVGElementEventMap, K, T>): void;
60
+ /**
61
+ * @see {@linkcode useEventListener}
62
+ * @ignore
63
+ */
64
+ export declare function useEventListener<K extends keyof MathMLElementEventMap, T extends MathMLElement>(...args: UseEventListenerOverloadArgs<MathMLElementEventMap, K, T>): void;
65
+ /**
66
+ * @see {@linkcode useEventListener}
67
+ */
68
+ export declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (this: Window, event: WindowEventMap[K]) => void, options?: AddEventListenerOptions | boolean): void;
69
+ /**
70
+ * @see {@linkcode useEventListener}
71
+ */
72
+ export declare function useEventListener<T extends EventTarget>(target: T | null, eventName: string, handler: (this: NoInfer<T>, event: Event) => void, options?: AddEventListenerOptions | boolean): void;
73
+ export {};
20
74
  //# sourceMappingURL=useEventListener.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../lib/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH;;;;;;GAMG;AAGH,iBAAS,gBAAgB,CACvB,CAAC,SAAS,MAAM,kBAAkB,EAClC,CAAC,SAAS,UAAU,EAEpB,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,kBAAkB,CAAC,CAAC,CAAC,KAAK,IAAI,EACxD,OAAO,EAAE,CAAC,GAAG,IAAI,EACjB,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,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,mBAAmB,CAAC,CAAC,CAAC,KAAK,IAAI,EACzD,OAAO,EAAE,CAAC,GAAG,IAAI,EACjB,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,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,KAAK,IAAI,EAC7D,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,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EACzD,OAAO,CAAC,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;AAGR,iBAAS,gBAAgB,CAAC,CAAC,SAAS,WAAW,EAC7C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,EACxC,OAAO,CAAC,EAAE,CAAC,GAAG,IAAI,EAClB,OAAO,CAAC,EAAE,OAAO,GAAG,uBAAuB,GAC1C,IAAI,CAAC;AAsCR,eAAe,gBAAgB,CAAC"}
1
+ {"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,KAAK,4BAA4B,CAC/B,QAAQ,EACR,CAAC,SAAS,MAAM,QAAQ,EACxB,CAAC,SAAS,WAAW,IACnB;IACF,MAAM,EAAE,CAAC,GAAG,IAAI;IAChB,SAAS,EAAE,CAAC;IACZ,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,IAAI;IACvD,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO;CAC5C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7D,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EACzD,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,GAC1C,IAAI,CAAC;AAER;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7D,GAAG,IAAI,EAAE,4BAA4B,CAAC,cAAc,EAAE,CAAC,EAAE,MAAM,CAAC,GAC/D,IAAI,CAAC;AAER;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,gBAAgB,EAC/D,GAAG,IAAI,EAAE,4BAA4B,CAAC,gBAAgB,EAAE,CAAC,EAAE,QAAQ,CAAC,GACnE,IAAI,CAAC;AAER;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,CAAC,SAAS,MAAM,mBAAmB,EACnC,CAAC,SAAS,WAAW,EACrB,GAAG,IAAI,EAAE,4BAA4B,CAAC,mBAAmB,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAE1E;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,CAAC,SAAS,MAAM,kBAAkB,EAClC,CAAC,SAAS,UAAU,EACpB,GAAG,IAAI,EAAE,4BAA4B,CAAC,kBAAkB,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAEzE;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,CAAC,SAAS,MAAM,qBAAqB,EACrC,CAAC,SAAS,aAAa,EACvB,GAAG,IAAI,EAAE,4BAA4B,CAAC,qBAAqB,EAAE,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAE5E;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,MAAM,cAAc,EAC7D,SAAS,EAAE,CAAC,EACZ,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,KAAK,IAAI,EACzD,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,GAC1C,IAAI,CAAC;AAER;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,WAAW,EACpD,MAAM,EAAE,CAAC,GAAG,IAAI,EAChB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,EACjD,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,GAC1C,IAAI,CAAC"}
@@ -4,31 +4,62 @@
4
4
  * @license MIT
5
5
  * @copyright 2020 Julien CARON
6
6
  */
7
- import { useEffect, useRef } from 'react';
8
- import useIsomorphicLayoutEffect from "./useIsomorphicLayoutEffect.js";
9
- function useEventListener(eventName, handler, element, options) {
10
- // Create a ref that stores handler
11
- const savedHandler = useRef(handler);
12
- useIsomorphicLayoutEffect(() => {
13
- savedHandler.current = handler;
14
- }, [handler]);
7
+ import { useEffect, useMemo, useRef } from 'react';
8
+ /**
9
+ * Adds `handler` as a listener for the event `eventName` of `target` with the
10
+ * provided `options` applied
11
+ *
12
+ * If `target` is not provided, `window` is used instead.
13
+ *
14
+ * If `target` is `null`, no event listener is added. This is useful when
15
+ * working with DOM element refs, or when the event listener needs to be removed
16
+ * temporarily.
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * useEventListener('resize', () => {
21
+ * console.log(window.innerWidth, window.innerHeight);
22
+ * });
23
+ *
24
+ * useEventListener(document, 'visibilitychange', () => {
25
+ * console.log(document.visibilityState);
26
+ * });
27
+ *
28
+ * const buttonRef = useRef<HTMLButtonElement>(null);
29
+ * useEventListener(buttonRef.current, 'click', () => console.log('click'));
30
+ * ```
31
+ */
32
+ export function useEventListener(...args) {
33
+ let eventName;
34
+ let handler;
35
+ let target;
36
+ let options;
37
+ if (typeof args[0] === 'string') {
38
+ [eventName, handler, options] = args;
39
+ }
40
+ else {
41
+ [target, eventName, handler, options] =
42
+ args;
43
+ }
44
+ const handlerRef = useRef(handler);
45
+ handlerRef.current = handler;
46
+ const { capture = false, once = false, passive, signal, } = typeof options === 'boolean' ? { capture: options } : (options ?? {});
47
+ const memoizedOptions = useMemo(() => options,
48
+ // eslint-disable-next-line react-hooks/exhaustive-deps
49
+ [capture, once, passive, signal]);
15
50
  useEffect(() => {
16
- if (element === null) {
51
+ if (target === null) {
17
52
  // No element has been attached to the ref yet
18
53
  return;
19
54
  }
20
- // Define the listening target
21
- const targetElement = element ?? window;
22
- // Create event listener that calls handler function stored in ref
55
+ const definedTarget = target ?? window;
23
56
  const listener = function (event) {
24
- savedHandler.current.call(this, event);
57
+ handlerRef.current.call(this, event);
25
58
  };
26
- targetElement.addEventListener(eventName, listener, options);
27
- // Remove event listener on cleanup
59
+ definedTarget.addEventListener(eventName, listener, memoizedOptions);
28
60
  return () => {
29
- targetElement.removeEventListener(eventName, listener, options);
61
+ definedTarget.removeEventListener(eventName, listener, memoizedOptions);
30
62
  };
31
- }, [eventName, element, options]);
63
+ }, [eventName, target, memoizedOptions]);
32
64
  }
33
- export default useEventListener;
34
65
  //# sourceMappingURL=useEventListener.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../lib/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,yBAAyB,MAAM,gCAAgC,CAAC;AAwDvE,SAAS,gBAAgB,CACvB,SAAiB,EACjB,OAAkD,EAClD,OAA4B,EAC5B,OAA2C;IAE3C,mCAAmC;IACnC,MAAM,YAAY,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAErC,yBAAyB,CAAC,GAAG,EAAE;QAC7B,YAAY,CAAC,OAAO,GAAG,OAAO,CAAC;IACjC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,8CAA8C;YAC9C,OAAO;QACT,CAAC;QAED,8BAA8B;QAC9B,MAAM,aAAa,GAAG,OAAO,IAAI,MAAM,CAAC;QAExC,kEAAkE;QAClE,MAAM,QAAQ,GAAmB,UAAU,KAAK;YAC9C,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACzC,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
+ {"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AA2GnD;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,gBAAgB,CAC9B,GAAG,IAAwE;IAE3E,IAAI,SAAiB,CAAC;IACtB,IAAI,OAAkD,CAAC;IACvD,IAAI,MAAsC,CAAC;IAC3C,IAAI,OAAsD,CAAC;IAE3D,IAAI,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;QAChC,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,IAAyC,CAAC;IAC5E,CAAC;SAAM,CAAC;QACN,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;YACnC,IAAsC,CAAC;IAC3C,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAE7B,MAAM,EACJ,OAAO,GAAG,KAAK,EACf,IAAI,GAAG,KAAK,EACZ,OAAO,EACP,MAAM,GACP,GAAG,OAAO,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IAE1E,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,OAAO;IACb,uDAAuD;IACvD,CAAC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CACjC,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,8CAA8C;YAC9C,OAAO;QACT,CAAC;QAED,MAAM,aAAa,GAAG,MAAM,IAAI,MAAM,CAAC;QAEvC,MAAM,QAAQ,GAAmB,UAAU,KAAK;YAC9C,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,aAAa,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAErE,OAAO,GAAG,EAAE;YACV,aAAa,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC1E,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,CAAC;AAC3C,CAAC"}
@@ -4,15 +4,63 @@
4
4
  * This hook is designed in the most general way possible in order to cover all
5
5
  * imaginable use cases.
6
6
  *
7
+ * @example
8
+ * Sometimes, React's immutability constraints mean too much unnecessary copying
9
+ * of data when new data arrives at a high frequency. In such cases, it might be
10
+ * desirable to ignore the constraints by embracing imperative patterns.
11
+ * Here is an example of a scenario where that can make sense:
12
+ *
13
+ * ```tsx
14
+ * type SensorData = { timestamp: number; value: number };
15
+ * const sensorDataRef = useRef<SensorData[]>([]);
16
+ * const mostRecentSensorDataTimestampRef = useRef<number>(0);
17
+ *
18
+ * const [forceUpdate, updateCount] = useForceUpdate();
19
+ * // Limiting the frequency of forced re-renders with some throttle function:
20
+ * const throttledForceUpdateRef = useRef(throttle(forceUpdate));
21
+ *
22
+ * useEffect(() => {
23
+ * return sensorDataObservable.subscribe((data: SensorData) => {
24
+ * // Imagine new sensor data arrives every 1 millisecond. If we were following
25
+ * // React's immutability rules by creating a new array every time, the data
26
+ * // that's already there would have to be copied many times before the new
27
+ * // data would even get a chance to be reflected in the UI for the first time
28
+ * // because it typically takes much longer than 1 millisecond for a new frame
29
+ * // to be displayed. To prevent the waste of computational resources, we just
30
+ * // mutate the existing array every time instead:
31
+ * sensorDataRef.current.push(data);
32
+ * if (data.timestamp > mostRecentSensorDataTimestampRef.current) {
33
+ * mostRecentSensorDataTimestampRef.current = data.timestamp;
34
+ * }
35
+ * throttledForceUpdateRef.current();
36
+ * });
37
+ * }, []);
38
+ *
39
+ * const [timeWindow, setTimeWindow] = useState(1000);
40
+ * const selectedSensorData = useMemo(
41
+ * () => {
42
+ * // Keep this line if you don't want to disable the
43
+ * // react-hooks/exhaustive-deps ESLint rule:
44
+ * updateCount;
45
+ * const threshold = mostRecentSensorDataTimestampRef.current - timeWindow;
46
+ * return sensorDataRef.current.filter(
47
+ * ({ timestamp }) => timestamp >= threshold,
48
+ * );
49
+ * },
50
+ * // sensorDataRef.current always references the same array, so listing it as a
51
+ * // dependency is pointless. Instead, updateCount should be used:
52
+ * [updateCount, timeWindow],
53
+ * );
54
+ * ```
55
+ *
7
56
  * @param callback An optional callback function to call during renders that
8
57
  * were triggered with `forceUpdate()`
9
58
  *
10
59
  * Can be used for conditionally calling state setters when state needs to be
11
60
  * reset. That is legal and better than using effects (see
12
61
  * {@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}).
62
+ * but can often be avoided by using {@linkcode useStateWithDeps} or
63
+ * {@linkcode useReducerWithDeps}.
16
64
  *
17
65
  * Important: the callback function is called once per render, not once per
18
66
  * `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
@@ -23,5 +71,5 @@
23
71
  * 1. A `forceUpdate` function that triggers a re-render
24
72
  * 2. The number of times `forceUpdate` has been called so far
25
73
  */
26
- export default function useForceUpdate(callback?: () => void): [() => void, bigint];
74
+ export declare function useForceUpdate(callback?: () => void): [() => void, bigint];
27
75
  //# sourceMappingURL=useForceUpdate.d.ts.map
@@ -1 +1 @@
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
+ {"version":3,"file":"useForceUpdate.d.ts","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,CAAC,CAU1E"}
@@ -1,19 +1,68 @@
1
1
  import { useReducer, useRef } from 'react';
2
+ /* eslint-enable */
2
3
  /**
3
4
  * Enables you to imperatively trigger re-rendering of components
4
5
  *
5
6
  * This hook is designed in the most general way possible in order to cover all
6
7
  * imaginable use cases.
7
8
  *
9
+ * @example
10
+ * Sometimes, React's immutability constraints mean too much unnecessary copying
11
+ * of data when new data arrives at a high frequency. In such cases, it might be
12
+ * desirable to ignore the constraints by embracing imperative patterns.
13
+ * Here is an example of a scenario where that can make sense:
14
+ *
15
+ * ```tsx
16
+ * type SensorData = { timestamp: number; value: number };
17
+ * const sensorDataRef = useRef<SensorData[]>([]);
18
+ * const mostRecentSensorDataTimestampRef = useRef<number>(0);
19
+ *
20
+ * const [forceUpdate, updateCount] = useForceUpdate();
21
+ * // Limiting the frequency of forced re-renders with some throttle function:
22
+ * const throttledForceUpdateRef = useRef(throttle(forceUpdate));
23
+ *
24
+ * useEffect(() => {
25
+ * return sensorDataObservable.subscribe((data: SensorData) => {
26
+ * // Imagine new sensor data arrives every 1 millisecond. If we were following
27
+ * // React's immutability rules by creating a new array every time, the data
28
+ * // that's already there would have to be copied many times before the new
29
+ * // data would even get a chance to be reflected in the UI for the first time
30
+ * // because it typically takes much longer than 1 millisecond for a new frame
31
+ * // to be displayed. To prevent the waste of computational resources, we just
32
+ * // mutate the existing array every time instead:
33
+ * sensorDataRef.current.push(data);
34
+ * if (data.timestamp > mostRecentSensorDataTimestampRef.current) {
35
+ * mostRecentSensorDataTimestampRef.current = data.timestamp;
36
+ * }
37
+ * throttledForceUpdateRef.current();
38
+ * });
39
+ * }, []);
40
+ *
41
+ * const [timeWindow, setTimeWindow] = useState(1000);
42
+ * const selectedSensorData = useMemo(
43
+ * () => {
44
+ * // Keep this line if you don't want to disable the
45
+ * // react-hooks/exhaustive-deps ESLint rule:
46
+ * updateCount;
47
+ * const threshold = mostRecentSensorDataTimestampRef.current - timeWindow;
48
+ * return sensorDataRef.current.filter(
49
+ * ({ timestamp }) => timestamp >= threshold,
50
+ * );
51
+ * },
52
+ * // sensorDataRef.current always references the same array, so listing it as a
53
+ * // dependency is pointless. Instead, updateCount should be used:
54
+ * [updateCount, timeWindow],
55
+ * );
56
+ * ```
57
+ *
8
58
  * @param callback An optional callback function to call during renders that
9
59
  * were triggered with `forceUpdate()`
10
60
  *
11
61
  * Can be used for conditionally calling state setters when state needs to be
12
62
  * reset. That is legal and better than using effects (see
13
63
  * {@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}).
64
+ * but can often be avoided by using {@linkcode useStateWithDeps} or
65
+ * {@linkcode useReducerWithDeps}.
17
66
  *
18
67
  * Important: the callback function is called once per render, not once per
19
68
  * `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
@@ -24,15 +73,15 @@ import { useReducer, useRef } from 'react';
24
73
  * 1. A `forceUpdate` function that triggers a re-render
25
74
  * 2. The number of times `forceUpdate` has been called so far
26
75
  */
27
- export default function useForceUpdate(callback) {
76
+ export function useForceUpdate(callback) {
28
77
  // It is very unlikely that the number of updates will exceed
29
78
  // 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;
79
+ const [updateCount, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
80
+ const updateCountRef = useRef(updateCount);
81
+ if (updateCount !== updateCountRef.current) {
82
+ updateCountRef.current = updateCount;
34
83
  callback?.();
35
84
  }
36
- return [forceUpdate, counter];
85
+ return [forceUpdate, updateCount];
37
86
  }
38
87
  //# sourceMappingURL=useForceUpdate.js.map
@@ -1 +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"}
1
+ {"version":3,"file":"useForceUpdate.js","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAK3C,mBAAmB;AAEnB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwEG;AACH,MAAM,UAAU,cAAc,CAAC,QAAqB;IAClD,6DAA6D;IAC7D,2EAA2E;IAC3E,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC;IACvE,MAAM,cAAc,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;IAC3C,IAAI,WAAW,KAAK,cAAc,CAAC,OAAO,EAAE,CAAC;QAC3C,cAAc,CAAC,OAAO,GAAG,WAAW,CAAC;QACrC,QAAQ,EAAE,EAAE,CAAC;IACf,CAAC;IACD,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AACpC,CAAC"}
@@ -1,8 +1,14 @@
1
- import { type ActionDispatch, type AnyActionArg, type DependencyList } from 'react';
1
+ import { type DependencyList } from 'react';
2
+ /** @ignore */
3
+ export type AnyActionArg = [] | [any];
4
+ /** @ignore */
5
+ export type ActionDispatch<ActionArg extends AnyActionArg> = (...args: ActionArg) => void;
2
6
  /**
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
7
+ * `useReducer` hook with an additional dependency array `deps` that resets the
8
+ * state to `initialState` when dependencies change
9
+ *
10
+ * For motivation and examples, see
11
+ * https://github.com/facebook/react/issues/33041.
6
12
  *
7
13
  * ### On linter support
8
14
  *
@@ -11,26 +17,26 @@ import { type ActionDispatch, type AnyActionArg, type DependencyList } from 'rea
11
17
  * However, as we would like to keep the hook as compatible with `useReducer` as
12
18
  * possible, we don't want to artificially change the parameter's position.
13
19
  * 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.
20
+ * Because of that, additional caution is advised!
21
+ * Be sure to check that no dependencies are missing from the `deps` array.
16
22
  *
17
23
  * Related issue: {@link https://github.com/facebook/react/issues/25443}.
18
24
  *
19
25
  * Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
20
26
  * `useExhaustiveDependencies` rule provided for Biome by Biome's team
21
27
  * does actually have support for dependency arrays at other positions, see
22
- * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
28
+ * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies useExhaustiveDependencies > Options > Validating dependencies}.
23
29
  *
24
30
  * @param reducer The reducer function that specifies how the state gets updated
25
31
  *
26
- * @param initialState The state that will be set when the component mounts or
27
- * the dependencies change
32
+ * @param initialState The value to which the state is set when the component is
33
+ * mounted or dependencies change
28
34
  *
29
- * It can also be a function which returns a state value. If the state is reset
35
+ * It can also be a function that returns a state value. If the state is reset
30
36
  * due to a change of dependencies, this function will be passed the previous
31
37
  * state as its argument (will be `undefined` in the first call upon mount).
32
38
  *
33
39
  * @param deps Dependencies that reset the state to `initialState`
34
40
  */
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>];
41
+ export declare function useReducerWithDeps<S, A extends AnyActionArg>(reducer: (prevState: S, ...args: A) => S, initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, ActionDispatch<A>];
36
42
  //# sourceMappingURL=useReducerWithDeps.d.ts.map
@@ -1 +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"}
1
+ {"version":3,"file":"useReducerWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,OAAO,CAAC;AAOjE,cAAc;AAEd,MAAM,MAAM,YAAY,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;AAEtC,cAAc;AACd,MAAM,MAAM,cAAc,CAAC,SAAS,SAAS,YAAY,IAAI,CAC3D,GAAG,IAAI,EAAE,SAAS,KACf,IAAI,CAAC;AAEV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,SAAS,YAAY,EAC1D,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"}
@@ -1,9 +1,11 @@
1
- import { useCallback, useRef, } from 'react';
2
- import useStateWithDeps from './useStateWithDeps.js';
1
+ import { useCallback, useRef } from 'react';
2
+ import { useStateWithDeps } from './useStateWithDeps.js';
3
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
4
+ * `useReducer` hook with an additional dependency array `deps` that resets the
5
+ * state to `initialState` when dependencies change
6
+ *
7
+ * For motivation and examples, see
8
+ * https://github.com/facebook/react/issues/33041.
7
9
  *
8
10
  * ### On linter support
9
11
  *
@@ -12,28 +14,28 @@ import useStateWithDeps from './useStateWithDeps.js';
12
14
  * However, as we would like to keep the hook as compatible with `useReducer` as
13
15
  * possible, we don't want to artificially change the parameter's position.
14
16
  * 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
+ * Because of that, additional caution is advised!
18
+ * Be sure to check that no dependencies are missing from the `deps` array.
17
19
  *
18
20
  * Related issue: {@link https://github.com/facebook/react/issues/25443}.
19
21
  *
20
22
  * Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
21
23
  * `useExhaustiveDependencies` rule provided for Biome by Biome's team
22
24
  * does actually have support for dependency arrays at other positions, see
23
- * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
25
+ * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies useExhaustiveDependencies > Options > Validating dependencies}.
24
26
  *
25
27
  * @param reducer The reducer function that specifies how the state gets updated
26
28
  *
27
- * @param initialState The state that will be set when the component mounts or
28
- * the dependencies change
29
+ * @param initialState The value to which the state is set when the component is
30
+ * mounted or dependencies change
29
31
  *
30
- * It can also be a function which returns a state value. If the state is reset
32
+ * It can also be a function that returns a state value. If the state is reset
31
33
  * due to a change of dependencies, this function will be passed the previous
32
34
  * state as its argument (will be `undefined` in the first call upon mount).
33
35
  *
34
36
  * @param deps Dependencies that reset the state to `initialState`
35
37
  */
36
- export default function useReducerWithDeps(reducer, initialState, deps) {
38
+ export function useReducerWithDeps(reducer, initialState, deps) {
37
39
  // eslint-disable-next-line react-hooks/exhaustive-deps
38
40
  const [state, setState] = useStateWithDeps(initialState, deps);
39
41
  // Only the initially provided reducer is used
@@ -1 +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,uBAAuB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"}
1
+ {"version":3,"file":"useReducerWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAezD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,MAAM,UAAU,kBAAkB,CAChC,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"}
@@ -6,18 +6,52 @@
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
+ * For motivation and more examples, see
13
+ * https://github.com/facebook/react/issues/33041.
15
14
  *
16
- * It can also be a function which returns a state value. If the state is reset
15
+ * @example
16
+ * ```tsx
17
+ * type Activity = 'breakfast' | 'exercise' | 'swim' | 'board games' | 'dinner';
18
+ *
19
+ * const timeOfDayOptions = ['morning', 'afternoon', 'evening'] as const;
20
+ * type TimeOfDay = (typeof timeOfDayOptions)[number];
21
+ *
22
+ * const activityOptionsByTimeOfDay: {
23
+ * [K in TimeOfDay]: [Activity, ...Activity[]];
24
+ * } = {
25
+ * morning: ['breakfast', 'exercise', 'swim'],
26
+ * afternoon: ['exercise', 'swim', 'board games'],
27
+ * evening: ['board games', 'dinner'],
28
+ * };
29
+ *
30
+ * export function Example() {
31
+ * const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
32
+ *
33
+ * const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
34
+ * const [activity, setActivity] = useStateWithDeps<Activity>(
35
+ * (prev) => {
36
+ * // Make sure activity is always valid for the current timeOfDay value,
37
+ * // but also don't reset it unless necessary:
38
+ * return prev && activityOptions.includes(prev) ? prev : activityOptions[0];
39
+ * },
40
+ * [activityOptions],
41
+ * );
42
+ *
43
+ * return '...';
44
+ * }
45
+ * ```
46
+ *
47
+ * @param initialState The value to which the state is set when the component is
48
+ * mounted or dependencies change
49
+ *
50
+ * It can also be a function that returns a state value. If the state is reset
17
51
  * due to a change of dependencies, this function will be passed the previous
18
52
  * state as its argument (will be `undefined` in the first call upon mount).
19
53
  *
20
54
  * @param deps Dependencies that reset the state to `initialState`
21
55
  */
22
- export default function useStateWithDeps<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
56
+ export declare function useStateWithDeps<S>(initialState: S | ((previousState?: S) => S), deps: DependencyList): [S, Dispatch<SetStateAction<S>>];
23
57
  //# 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;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,CA0ClC"}