@aweebit/react-essentials 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -1,8 +1,6 @@
1
1
  MIT License
2
2
 
3
3
  Copyright (c) 2025 Wee Bit
4
- Portions Copyright (c) 2020 Peter Juras
5
- Portions Copyright (c) 2020 Julien CARON
6
4
 
7
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
8
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -32,7 +32,7 @@
32
32
  const useEventListener: UseEventListener;
33
33
  ```
34
34
 
35
- Defined in: [hooks/useEventListener.ts:133](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L133)
35
+ Defined in: [hooks/useEventListener.ts:135](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L135)
36
36
 
37
37
  Adds `handler` as a listener for the event `eventName` of `target` with the
38
38
  provided `options` applied
@@ -73,68 +73,24 @@ useEventListener(buttonRef, 'click', () => console.log('click'));
73
73
 
74
74
  ---
75
75
 
76
- ## useForceUpdate()
76
+ ## ~~useForceUpdate()~~
77
77
 
78
78
  ```ts
79
79
  function useForceUpdate(callback?): [() => void, bigint];
80
80
  ```
81
81
 
82
- Defined in: [hooks/useForceUpdate.ts:81](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useForceUpdate.ts#L81)
82
+ Defined in: [hooks/useForceUpdate.ts:37](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useForceUpdate.ts#L37)
83
83
 
84
84
  Enables you to imperatively trigger re-rendering of components
85
85
 
86
86
  This hook is designed in the most general way possible in order to cover all
87
87
  imaginable use cases.
88
88
 
89
- ### Example
90
-
91
- Sometimes, React's immutability constraints mean too much unnecessary copying
92
- of data when new data arrives at a high frequency. In such cases, it might be
93
- desirable to ignore the constraints by embracing imperative patterns.
94
- Here is an example of a scenario where that can make sense:
89
+ ### Deprecated
95
90
 
96
- ```tsx
97
- type SensorData = { timestamp: number; value: number };
98
- const sensorDataRef = useRef<SensorData[]>([]);
99
- const mostRecentSensorDataTimestampRef = useRef<number>(0);
100
-
101
- const [forceUpdate, updateCount] = useForceUpdate();
102
- // Limiting the frequency of forced re-renders with some throttle function:
103
- const throttledForceUpdateRef = useRef(throttle(forceUpdate));
104
-
105
- useEffect(() => {
106
- return sensorDataObservable.subscribe((data: SensorData) => {
107
- // Imagine new sensor data arrives every 1 millisecond. If we were following
108
- // React's immutability rules by creating a new array every time, the data
109
- // that's already there would have to be copied many times before the new
110
- // data would even get a chance to be reflected in the UI for the first time
111
- // because it typically takes much longer than 1 millisecond for a new frame
112
- // to be displayed. To prevent the waste of computational resources, we just
113
- // mutate the existing array every time instead:
114
- sensorDataRef.current.push(data);
115
- if (data.timestamp > mostRecentSensorDataTimestampRef.current) {
116
- mostRecentSensorDataTimestampRef.current = data.timestamp;
117
- }
118
- throttledForceUpdateRef.current();
119
- });
120
- }, []);
121
-
122
- const [timeWindow, setTimeWindow] = useState(1000);
123
- const selectedSensorData = useMemo(
124
- () => {
125
- // Keep this line if you don't want to disable the
126
- // react-hooks/exhaustive-deps ESLint rule:
127
- updateCount;
128
- const threshold = mostRecentSensorDataTimestampRef.current - timeWindow;
129
- return sensorDataRef.current.filter(
130
- ({ timestamp }) => timestamp >= threshold,
131
- );
132
- },
133
- // sensorDataRef.current always references the same array, so listing it as a
134
- // dependency is pointless. Instead, updateCount should be used:
135
- [updateCount, timeWindow],
136
- );
137
- ```
91
+ This hook encourages patterns that are unsafe in Concurrent React.
92
+ For details and ideas on how to get rid of it, please check the discussion at
93
+ https://www.reddit.com/r/react/comments/1nqcsri/comment/ng76cv5/.
138
94
 
139
95
  ### Parameters
140
96
 
@@ -199,11 +155,18 @@ function useReducerWithDeps<S, A>(
199
155
  ): [S, ActionDispatch<A>];
200
156
  ```
201
157
 
202
- Defined in: [hooks/useReducerWithDeps.ts:52](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useReducerWithDeps.ts#L52)
158
+ Defined in: [hooks/useReducerWithDeps.ts:59](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useReducerWithDeps.ts#L59)
203
159
 
204
160
  `useReducer` hook with an additional dependency array `deps` that resets the
205
161
  state to `initialState` when dependencies change
206
162
 
163
+ This hook is the reducer pattern counterpart of [`useStateWithDeps`](#usestatewithdeps).
164
+
165
+ Due to React's limitations, a change in dependencies always causes two
166
+ renders when using this hook. The result of the first render is thrown away
167
+ as described in
168
+ [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
169
+
207
170
  For motivation and examples, see
208
171
  https://github.com/facebook/react/issues/33041.
209
172
 
@@ -335,11 +298,16 @@ function useStateWithDeps<S>(
335
298
  ): [S, Dispatch<SetStateAction<S>>];
336
299
  ```
337
300
 
338
- Defined in: [hooks/useStateWithDeps.ts:65](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useStateWithDeps.ts#L65)
301
+ Defined in: [hooks/useStateWithDeps.ts:62](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useStateWithDeps.ts#L62)
339
302
 
340
303
  `useState` hook with an additional dependency array `deps` that resets the
341
304
  state to `initialState` when dependencies change
342
305
 
306
+ Due to React's limitations, a change in dependencies always causes two
307
+ renders when using this hook. The result of the first render is thrown away
308
+ as described in
309
+ [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
310
+
343
311
  For motivation and more examples, see
344
312
  https://github.com/facebook/react/issues/33041.
345
313
 
@@ -359,7 +327,7 @@ const activityOptionsByTimeOfDay: {
359
327
  evening: ['board games', 'dinner'],
360
328
  };
361
329
 
362
- export function Example() {
330
+ function Example() {
363
331
  const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
364
332
 
365
333
  const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
@@ -462,7 +430,7 @@ function createSafeContext<T>(): <DisplayName>(
462
430
  ) => SafeContext<DisplayName, T>;
463
431
  ```
464
432
 
465
- Defined in: [misc/createSafeContext.ts:95](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/misc/createSafeContext.ts#L95)
433
+ Defined in: [misc/createSafeContext.ts:95](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/misc/createSafeContext.ts#L95)
466
434
 
467
435
  For a given type `T`, returns a function that produces both a context of that
468
436
  type and a hook that returns the current context value if one was provided,
@@ -609,15 +577,11 @@ A function that accepts a single string argument `displayName` (e.g.
609
577
 
610
578
  ```ts
611
579
  type UseEventListener = UseEventListenerWithImplicitWindowTarget &
612
- UseEventListenerWithExplicitTarget<Window, WindowEventMap> &
613
- UseEventListenerWithExplicitTarget<Document, DocumentEventMap> &
614
- UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> &
615
- UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> &
616
- UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap> &
580
+ UseEventListenerWithExplicitGlobalTarget &
617
581
  UseEventListenerWithAnyExplicitTarget;
618
582
  ```
619
583
 
620
- Defined in: [hooks/useEventListener.ts:19](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L19)
584
+ Defined in: [hooks/useEventListener.ts:12](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L12)
621
585
 
622
586
  The type of [`useEventListener`](#useeventlistener-1)
623
587
 
@@ -625,7 +589,7 @@ The type of [`useEventListener`](#useeventlistener-1)
625
589
 
626
590
  [`useEventListener`](#useeventlistener-1),
627
591
  [`UseEventListenerWithImplicitWindowTarget`](#useeventlistenerwithimplicitwindowtarget),
628
- [`UseEventListenerWithExplicitTarget`](#useeventlistenerwithexplicittarget),
592
+ [`UseEventListenerWithExplicitGlobalTarget`](#useeventlistenerwithexplicitglobaltarget),
629
593
  [`UseEventListenerWithAnyExplicitTarget`](#useeventlistenerwithanyexplicittarget)
630
594
 
631
595
  ---
@@ -636,7 +600,7 @@ The type of [`useEventListener`](#useeventlistener-1)
636
600
  type UseEventListenerWithImplicitWindowTarget = <K>(...args) => void;
637
601
  ```
638
602
 
639
- Defined in: [hooks/useEventListener.ts:31](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L31)
603
+ Defined in: [hooks/useEventListener.ts:21](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L21)
640
604
 
641
605
  ### Type Parameters
642
606
 
@@ -693,6 +657,26 @@ Defined in: [hooks/useEventListener.ts:31](https://github.com/aweebit/react-esse
693
657
 
694
658
  ---
695
659
 
660
+ ## UseEventListenerWithExplicitGlobalTarget
661
+
662
+ ```ts
663
+ type UseEventListenerWithExplicitGlobalTarget =
664
+ UseEventListenerWithExplicitTarget<Window, WindowEventMap> &
665
+ UseEventListenerWithExplicitTarget<Document, DocumentEventMap> &
666
+ UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> &
667
+ UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> &
668
+ UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap>;
669
+ ```
670
+
671
+ Defined in: [hooks/useEventListener.ts:32](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L32)
672
+
673
+ ### See
674
+
675
+ [`useEventListener`](#useeventlistener-1),
676
+ [`UseEventListenerWithExplicitTarget`](#useeventlistenerwithexplicittarget)
677
+
678
+ ---
679
+
696
680
  ## UseEventListenerWithExplicitTarget()
697
681
 
698
682
  ```ts
@@ -701,7 +685,7 @@ type UseEventListenerWithExplicitTarget<Target, EventMap> = <T, K>(
701
685
  ) => void;
702
686
  ```
703
687
 
704
- Defined in: [hooks/useEventListener.ts:42](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L42)
688
+ Defined in: [hooks/useEventListener.ts:44](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L44)
705
689
 
706
690
  ### Type Parameters
707
691
 
@@ -809,7 +793,7 @@ type UseEventListenerWithAnyExplicitTarget =
809
793
  UseEventListenerWithExplicitTarget<EventTarget>;
810
794
  ```
811
795
 
812
- Defined in: [hooks/useEventListener.ts:54](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L54)
796
+ Defined in: [hooks/useEventListener.ts:56](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L56)
813
797
 
814
798
  ### See
815
799
 
@@ -830,7 +814,7 @@ type UseEventListenerWithImplicitWindowTargetArgs<K> =
830
814
  : never;
831
815
  ```
832
816
 
833
- Defined in: [hooks/useEventListener.ts:62](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L62)
817
+ Defined in: [hooks/useEventListener.ts:64](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L64)
834
818
 
835
819
  ### Type Parameters
836
820
 
@@ -875,7 +859,7 @@ type UseEventListenerWithExplicitTargetArgs<EventMap, T, K> = [
875
859
  ];
876
860
  ```
877
861
 
878
- Defined in: [hooks/useEventListener.ts:76](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/hooks/useEventListener.ts#L76)
862
+ Defined in: [hooks/useEventListener.ts:78](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/hooks/useEventListener.ts#L78)
879
863
 
880
864
  ### Type Parameters
881
865
 
@@ -931,7 +915,7 @@ type RestrictedContext<T> =
931
915
  };
932
916
  ```
933
917
 
934
- Defined in: [misc/createSafeContext.ts:18](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/misc/createSafeContext.ts#L18)
918
+ Defined in: [misc/createSafeContext.ts:18](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/misc/createSafeContext.ts#L18)
935
919
 
936
920
  A React context with a required `displayName` and the obsolete `Consumer`
937
921
  property purposefully omitted so that it is impossible to pass the context
@@ -971,7 +955,7 @@ type SafeContext<DisplayName, T> = {
971
955
  } & { [K in `use${DisplayName}`]: () => T };
972
956
  ```
973
957
 
974
- Defined in: [misc/createSafeContext.ts:30](https://github.com/aweebit/react-essentials/blob/v0.8.0/src/misc/createSafeContext.ts#L30)
958
+ Defined in: [misc/createSafeContext.ts:30](https://github.com/aweebit/react-essentials/blob/v0.8.1/src/misc/createSafeContext.ts#L30)
975
959
 
976
960
  The return type of [`createSafeContext`](#createsafecontext)
977
961
 
@@ -1,9 +1,3 @@
1
- /**
2
- * @file Based on {@link https://github.com/juliencrn/usehooks-ts}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Julien CARON
6
- */
7
1
  import { type RefObject } from 'react';
8
2
  /**
9
3
  * The type of {@linkcode useEventListener}
@@ -11,15 +5,22 @@ import { type RefObject } from 'react';
11
5
  * @see
12
6
  * {@linkcode useEventListener},
13
7
  * {@linkcode UseEventListenerWithImplicitWindowTarget},
14
- * {@linkcode UseEventListenerWithExplicitTarget},
8
+ * {@linkcode UseEventListenerWithExplicitGlobalTarget},
15
9
  * {@linkcode UseEventListenerWithAnyExplicitTarget}
16
10
  */
17
- export type UseEventListener = UseEventListenerWithImplicitWindowTarget & UseEventListenerWithExplicitTarget<Window, WindowEventMap> & UseEventListenerWithExplicitTarget<Document, DocumentEventMap> & UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> & UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> & UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap> & UseEventListenerWithAnyExplicitTarget;
11
+ export type UseEventListener = UseEventListenerWithImplicitWindowTarget & UseEventListenerWithExplicitGlobalTarget & UseEventListenerWithAnyExplicitTarget;
18
12
  /**
19
13
  * @see
20
14
  * {@linkcode useEventListener},
21
- * {@linkcode UseEventListenerWithImplicitWindowTargetArgs} */
15
+ * {@linkcode UseEventListenerWithImplicitWindowTargetArgs}
16
+ */
22
17
  export type UseEventListenerWithImplicitWindowTarget = <K extends keyof WindowEventMap>(...args: UseEventListenerWithImplicitWindowTargetArgs<K>) => void;
18
+ /**
19
+ * @see
20
+ * {@linkcode useEventListener},
21
+ * {@linkcode UseEventListenerWithExplicitTarget}
22
+ */
23
+ export type UseEventListenerWithExplicitGlobalTarget = UseEventListenerWithExplicitTarget<Window, WindowEventMap> & UseEventListenerWithExplicitTarget<Document, DocumentEventMap> & UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> & UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> & UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap>;
23
24
  /**
24
25
  * @see
25
26
  * {@linkcode useEventListener},
@@ -1 +1 @@
1
- {"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,wCAAwC,GACrE,kCAAkC,CAAC,MAAM,EAAE,cAAc,CAAC,GAC1D,kCAAkC,CAAC,QAAQ,EAAE,gBAAgB,CAAC,GAC9D,kCAAkC,CAAC,WAAW,EAAE,mBAAmB,CAAC,GACpE,kCAAkC,CAAC,UAAU,EAAE,kBAAkB,CAAC,GAClE,kCAAkC,CAAC,aAAa,EAAE,qBAAqB,CAAC,GACxE,qCAAqC,CAAC;AAExC;;;8DAG8D;AAC9D,MAAM,MAAM,wCAAwC,GAAG,CACrD,CAAC,SAAS,MAAM,cAAc,EAE9B,GAAG,IAAI,EAAE,4CAA4C,CAAC,CAAC,CAAC,KACrD,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,kCAAkC,CAC5C,MAAM,SAAS,WAAW,EAC1B,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAC9B,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,QAAQ,EAC7C,GAAG,IAAI,EAAE,sCAAsC,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,KAC5D,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,qCAAqC,GAC/C,kCAAkC,CAAC,WAAW,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,4CAA4C,CACtD,CAAC,SAAS,MAAM,cAAc,IAE9B,sCAAsC,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS;IACxE,OAAO;IACP,GAAG,MAAM,IAAI;CACd,GACG,IAAI,GACJ,KAAK,CAAC;AAEZ;;;GAGG;AACH,MAAM,MAAM,sCAAsC,CAChD,QAAQ,EACR,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,MAAM,QAAQ,IACtB;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;QAAE,gBAAgB,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,GAAG,IAAI;IAChE,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,GAAG,SAAS;CACxD,CAAC;AAYF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,gBAAgB,EAAE,gBAkDV,CAAC"}
1
+ {"version":3,"file":"useEventListener.d.ts","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,MAAM,gBAAgB,GAAG,wCAAwC,GACrE,wCAAwC,GACxC,qCAAqC,CAAC;AAExC;;;;GAIG;AACH,MAAM,MAAM,wCAAwC,GAAG,CACrD,CAAC,SAAS,MAAM,cAAc,EAE9B,GAAG,IAAI,EAAE,4CAA4C,CAAC,CAAC,CAAC,KACrD,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,wCAAwC,GAClD,kCAAkC,CAAC,MAAM,EAAE,cAAc,CAAC,GACxD,kCAAkC,CAAC,QAAQ,EAAE,gBAAgB,CAAC,GAC9D,kCAAkC,CAAC,WAAW,EAAE,mBAAmB,CAAC,GACpE,kCAAkC,CAAC,UAAU,EAAE,kBAAkB,CAAC,GAClE,kCAAkC,CAAC,aAAa,EAAE,qBAAqB,CAAC,CAAC;AAE7E;;;;GAIG;AACH,MAAM,MAAM,kCAAkC,CAC5C,MAAM,SAAS,WAAW,EAC1B,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAC9B,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,QAAQ,EAC7C,GAAG,IAAI,EAAE,sCAAsC,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC,CAAC,KAC5D,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,MAAM,qCAAqC,GAC/C,kCAAkC,CAAC,WAAW,CAAC,CAAC;AAElD;;;;GAIG;AACH,MAAM,MAAM,4CAA4C,CACtD,CAAC,SAAS,MAAM,cAAc,IAE9B,sCAAsC,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC,CAAC,SAAS;IACxE,OAAO;IACP,GAAG,MAAM,IAAI;CACd,GACG,IAAI,GACJ,KAAK,CAAC;AAEZ;;;GAGG;AACH,MAAM,MAAM,sCAAsC,CAChD,QAAQ,EACR,CAAC,SAAS,WAAW,EACrB,CAAC,SAAS,MAAM,QAAQ,IACtB;IACF,MAAM,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG;QAAE,gBAAgB,CAAC,EAAE,KAAK,CAAA;KAAE,CAAC,GAAG,IAAI;IAChE,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,GAAG,SAAS;CACxD,CAAC;AAYF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,eAAO,MAAM,gBAAgB,EAAE,gBAoDV,CAAC"}
@@ -1,9 +1,3 @@
1
- /**
2
- * @file Based on {@link https://github.com/juliencrn/usehooks-ts}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Julien CARON
6
- */
7
1
  import { useEffect, useMemo, useRef } from 'react';
8
2
  /**
9
3
  * Adds `handler` as a listener for the event `eventName` of `target` with the
@@ -47,7 +41,9 @@ export const useEventListener = function useEventListener(...args) {
47
41
  : args;
48
42
  const unwrappedTarget = target && !('addEventListener' in target) ? target.current : target;
49
43
  const handlerRef = useRef(handler);
50
- handlerRef.current = handler;
44
+ useEffect(() => {
45
+ handlerRef.current = handler;
46
+ }, [handler]);
51
47
  const { capture = false, once = false, passive, signal, } = typeof options === 'boolean' ? { capture: options } : (options ?? {});
52
48
  const memoizedOptions = useMemo(() => options,
53
49
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -1 +1 @@
1
- {"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AAyFnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqB,SAAS,gBAAgB,CACzE,GAAG,IAE0C;IAE7C,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GAMzC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QACzB,CAAC,CAAC,CAAC,MAAM,EAAE,GAAI,IAAwD,CAAC;QACxE,CAAC,CAAE,IAAkD,CAAC;IAE1D,MAAM,eAAe,GACnB,MAAM,IAAI,CAAC,CAAC,kBAAkB,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAEtE,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,eAAe,KAAK,IAAI,EAAE,CAAC;YAC7B,8CAA8C;YAC9C,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAmB,UAAU,KAAK;YAC9C,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,eAAe,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAEvE,OAAO,GAAG,EAAE;YACV,eAAe,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC5E,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;AACpD,CAAqB,CAAC"}
1
+ {"version":3,"file":"useEventListener.js","sourceRoot":"","sources":["../../src/hooks/useEventListener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAkB,MAAM,OAAO,CAAC;AAkGnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAqB,SAAS,gBAAgB,CACzE,GAAG,IAE0C;IAE7C,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC,GAMzC,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,QAAQ;QACzB,CAAC,CAAC,CAAC,MAAM,EAAE,GAAI,IAAwD,CAAC;QACxE,CAAC,CAAE,IAAkD,CAAC;IAE1D,MAAM,eAAe,GACnB,MAAM,IAAI,CAAC,CAAC,kBAAkB,IAAI,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAEtE,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC/B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,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,eAAe,KAAK,IAAI,EAAE,CAAC;YAC7B,8CAA8C;YAC9C,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,GAAmB,UAAU,KAAK;YAC9C,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACvC,CAAC,CAAC;QAEF,eAAe,CAAC,gBAAgB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAEvE,OAAO,GAAG,EAAE;YACV,eAAe,CAAC,mBAAmB,CAAC,SAAS,EAAE,QAAQ,EAAE,eAAe,CAAC,CAAC;QAC5E,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,eAAe,EAAE,SAAS,EAAE,eAAe,CAAC,CAAC,CAAC;AACpD,CAAqB,CAAC"}
@@ -4,54 +4,10 @@
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
- * ```
7
+ * @deprecated
8
+ * This hook encourages patterns that are unsafe in Concurrent React.
9
+ * For details and ideas on how to get rid of it, please check the discussion at
10
+ * https://www.reddit.com/r/react/comments/1nqcsri/comment/ng76cv5/.
55
11
  *
56
12
  * @param callback An optional callback function to call during renders that
57
13
  * were triggered with `forceUpdate()`
@@ -1 +1 @@
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
+ {"version":3,"file":"useForceUpdate.d.ts","sourceRoot":"","sources":["../../src/hooks/useForceUpdate.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,EAAE,MAAM,CAAC,CAU1E"}
@@ -6,54 +6,10 @@ import { useReducer, useRef } from 'react';
6
6
  * This hook is designed in the most general way possible in order to cover all
7
7
  * imaginable use cases.
8
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
- * ```
9
+ * @deprecated
10
+ * This hook encourages patterns that are unsafe in Concurrent React.
11
+ * For details and ideas on how to get rid of it, please check the discussion at
12
+ * https://www.reddit.com/r/react/comments/1nqcsri/comment/ng76cv5/.
57
13
  *
58
14
  * @param callback An optional callback function to call during renders that
59
15
  * were triggered with `forceUpdate()`
@@ -1 +1 @@
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
+ {"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;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"}
@@ -7,6 +7,13 @@ export type ActionDispatch<ActionArg extends AnyActionArg> = (...args: ActionArg
7
7
  * `useReducer` hook with an additional dependency array `deps` that resets the
8
8
  * state to `initialState` when dependencies change
9
9
  *
10
+ * This hook is the reducer pattern counterpart of {@linkcode useStateWithDeps}.
11
+ *
12
+ * Due to React's limitations, a change in dependencies always causes two
13
+ * renders when using this hook. The result of the first render is thrown away
14
+ * as described in
15
+ * [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
16
+ *
10
17
  * For motivation and examples, see
11
18
  * https://github.com/facebook/react/issues/33041.
12
19
  *
@@ -1 +1 @@
1
- {"version":3,"file":"useReducerWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,OAAO,CAAC;AAOpD,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
+ {"version":3,"file":"useReducerWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAU,MAAM,OAAO,CAAC;AAOpD,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;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"}
@@ -4,6 +4,13 @@ import { useStateWithDeps } from './useStateWithDeps.js';
4
4
  * `useReducer` hook with an additional dependency array `deps` that resets the
5
5
  * state to `initialState` when dependencies change
6
6
  *
7
+ * This hook is the reducer pattern counterpart of {@linkcode useStateWithDeps}.
8
+ *
9
+ * Due to React's limitations, a change in dependencies always causes two
10
+ * renders when using this hook. The result of the first render is thrown away
11
+ * as described in
12
+ * [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
13
+ *
7
14
  * For motivation and examples, see
8
15
  * https://github.com/facebook/react/issues/33041.
9
16
  *
@@ -40,9 +47,9 @@ export function useReducerWithDeps(reducer, initialState, deps) {
40
47
  const [state, setState] = useStateWithDeps(initialState, deps);
41
48
  // Only the initially provided reducer is used
42
49
  const reducerRef = useRef(reducer);
43
- const dispatch = useRef(function dispatch(...args) {
50
+ function dispatch(...args) {
44
51
  setState((previousState) => reducerRef.current(previousState, ...args));
45
- }).current;
46
- return [state, dispatch];
52
+ }
53
+ return [state, useRef(dispatch).current];
47
54
  }
48
55
  //# sourceMappingURL=useReducerWithDeps.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useReducerWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useReducerWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,MAAM,EAAE,MAAM,OAAO,CAAC;AACpD,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,MAAM,CAAC,SAAS,QAAQ,CAAC,GAAG,IAAO;QAClD,QAAQ,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC,OAAO,CAAC;IAEX,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,MAAM,EAAE,MAAM,OAAO,CAAC;AACpD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAezD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;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,SAAS,QAAQ,CAAC,GAAG,IAAO;QAC1B,QAAQ,CAAC,CAAC,aAAa,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,aAAa,EAAE,GAAG,IAAI,CAAC,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC"}
@@ -1,14 +1,13 @@
1
- /**
2
- * @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Peter Juras
6
- */
7
1
  import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
8
2
  /**
9
3
  * `useState` hook with an additional dependency array `deps` that resets the
10
4
  * state to `initialState` when dependencies change
11
5
  *
6
+ * Due to React's limitations, a change in dependencies always causes two
7
+ * renders when using this hook. The result of the first render is thrown away
8
+ * as described in
9
+ * [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
10
+ *
12
11
  * For motivation and more examples, see
13
12
  * https://github.com/facebook/react/issues/33041.
14
13
  *
@@ -27,7 +26,7 @@ import { type DependencyList, type Dispatch, type SetStateAction } from 'react';
27
26
  * evening: ['board games', 'dinner'],
28
27
  * };
29
28
  *
30
- * export function Example() {
29
+ * function Example() {
31
30
  * const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
32
31
  *
33
32
  * const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
@@ -1 +1 @@
1
- {"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAEL,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"}
1
+ {"version":3,"file":"useStateWithDeps.d.ts","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,QAAQ,EACb,KAAK,cAAc,EACpB,MAAM,OAAO,CAAC;AAGf;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;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,CAWlC"}
@@ -1,16 +1,14 @@
1
- /**
2
- * @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Peter Juras
6
- */
7
- import { useRef, } from 'react';
8
- import { depsAreEqual, isFunction } from '../utils.js';
9
- import { useForceUpdate } from './useForceUpdate.js';
1
+ import { useState, } from 'react';
2
+ import { depsAreEqual } from '../utils.js';
10
3
  /**
11
4
  * `useState` hook with an additional dependency array `deps` that resets the
12
5
  * state to `initialState` when dependencies change
13
6
  *
7
+ * Due to React's limitations, a change in dependencies always causes two
8
+ * renders when using this hook. The result of the first render is thrown away
9
+ * as described in
10
+ * [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
11
+ *
14
12
  * For motivation and more examples, see
15
13
  * https://github.com/facebook/react/issues/33041.
16
14
  *
@@ -29,7 +27,7 @@ import { useForceUpdate } from './useForceUpdate.js';
29
27
  * evening: ['board games', 'dinner'],
30
28
  * };
31
29
  *
32
- * export function Example() {
30
+ * function Example() {
33
31
  * const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
34
32
  *
35
33
  * const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
@@ -56,41 +54,12 @@ import { useForceUpdate } from './useForceUpdate.js';
56
54
  * @param deps Dependencies that reset the state to `initialState`
57
55
  */
58
56
  export function useStateWithDeps(initialState, deps) {
59
- // It would be possible to use useState instead of useRef to store the state,
60
- // however this would trigger re-renders whenever the state is reset due to a
61
- // change in dependencies. In order to avoid these re-renders, the state is
62
- // stored in a ref, and updates are triggered with forceUpdate when necessary.
63
- const state = useRef(undefined);
64
- const prevDeps = useRef(deps);
65
- const isMounted = useRef(false);
66
- // If first render, or if dependencies have changed since last time
67
- if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
68
- // Update state and deps
69
- let nextState;
70
- if (isFunction(initialState)) {
71
- nextState = initialState(state.current);
72
- }
73
- else {
74
- nextState = initialState;
75
- }
76
- state.current = nextState;
77
- prevDeps.current = deps;
78
- isMounted.current = true;
57
+ const [state, setState] = useState(initialState);
58
+ const [prevDeps, setPrevDeps] = useState(deps);
59
+ if (!depsAreEqual(deps, prevDeps)) {
60
+ setPrevDeps(deps);
61
+ setState(initialState);
79
62
  }
80
- const [forceUpdate] = useForceUpdate();
81
- const updateState = useRef(function updateState(newState) {
82
- let nextState;
83
- if (isFunction(newState)) {
84
- nextState = newState(state.current);
85
- }
86
- else {
87
- nextState = newState;
88
- }
89
- if (!Object.is(state.current, nextState)) {
90
- state.current = nextState;
91
- forceUpdate();
92
- }
93
- }).current;
94
- return [state.current, updateState];
63
+ return [state, setState];
95
64
  }
96
65
  //# sourceMappingURL=useStateWithDeps.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useStateWithDeps.js","sourceRoot":"","sources":["../../src/hooks/useStateWithDeps.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EACL,MAAM,GAIP,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAA4C,EAC5C,IAAoB;IAEpB,6EAA6E;IAC7E,6EAA6E;IAC7E,2EAA2E;IAC3E,8EAA8E;IAC9E,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,MAAM,CAAC,SAAS,WAAW,CAC7C,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,CAAC,CAAC,OAAO,CAAC;IAEX,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,OAAO,EACL,QAAQ,GAIT,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAA4C,EAC5C,IAAoB;IAEpB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IAEjD,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAE/C,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;QAClC,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,QAAQ,CAAC,YAAY,CAAC,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;AAC3B,CAAC"}
package/dist/utils.d.ts CHANGED
@@ -1,7 +1,4 @@
1
1
  import type { DependencyList } from 'react';
2
- export type Callable = (...args: never) => unknown;
3
2
  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
3
  export declare function depsAreEqual(prevDeps: DependencyList, deps: DependencyList): boolean;
7
4
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +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
+ {"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,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,YAAY,CAC1B,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,cAAc,GACnB,OAAO,CAKT"}
package/dist/utils.js CHANGED
@@ -1,7 +1,3 @@
1
- export function noop() { }
2
- export function isFunction(input) {
3
- return typeof input === 'function';
4
- }
5
1
  export function depsAreEqual(prevDeps, deps) {
6
2
  return (prevDeps.length === deps.length &&
7
3
  deps.every((dep, index) => Object.is(dep, prevDeps[index])));
package/dist/utils.js.map CHANGED
@@ -1 +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"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAQA,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.8.0",
3
+ "version": "0.8.1",
4
4
  "type": "module",
5
5
  "repository": "github:aweebit/react-essentials",
6
6
  "main": "dist/index.js",
@@ -1,10 +1,3 @@
1
- /**
2
- * @file Based on {@link https://github.com/juliencrn/usehooks-ts}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Julien CARON
6
- */
7
-
8
1
  import { useEffect, useMemo, useRef, type RefObject } from 'react';
9
2
 
10
3
  /**
@@ -13,27 +6,36 @@ import { useEffect, useMemo, useRef, type RefObject } from 'react';
13
6
  * @see
14
7
  * {@linkcode useEventListener},
15
8
  * {@linkcode UseEventListenerWithImplicitWindowTarget},
16
- * {@linkcode UseEventListenerWithExplicitTarget},
9
+ * {@linkcode UseEventListenerWithExplicitGlobalTarget},
17
10
  * {@linkcode UseEventListenerWithAnyExplicitTarget}
18
11
  */
19
12
  export type UseEventListener = UseEventListenerWithImplicitWindowTarget &
20
- UseEventListenerWithExplicitTarget<Window, WindowEventMap> &
21
- UseEventListenerWithExplicitTarget<Document, DocumentEventMap> &
22
- UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> &
23
- UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> &
24
- UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap> &
13
+ UseEventListenerWithExplicitGlobalTarget &
25
14
  UseEventListenerWithAnyExplicitTarget;
26
15
 
27
16
  /**
28
17
  * @see
29
18
  * {@linkcode useEventListener},
30
- * {@linkcode UseEventListenerWithImplicitWindowTargetArgs} */
19
+ * {@linkcode UseEventListenerWithImplicitWindowTargetArgs}
20
+ */
31
21
  export type UseEventListenerWithImplicitWindowTarget = <
32
22
  K extends keyof WindowEventMap,
33
23
  >(
34
24
  ...args: UseEventListenerWithImplicitWindowTargetArgs<K>
35
25
  ) => void;
36
26
 
27
+ /**
28
+ * @see
29
+ * {@linkcode useEventListener},
30
+ * {@linkcode UseEventListenerWithExplicitTarget}
31
+ */
32
+ export type UseEventListenerWithExplicitGlobalTarget =
33
+ UseEventListenerWithExplicitTarget<Window, WindowEventMap> &
34
+ UseEventListenerWithExplicitTarget<Document, DocumentEventMap> &
35
+ UseEventListenerWithExplicitTarget<HTMLElement, HTMLElementEventMap> &
36
+ UseEventListenerWithExplicitTarget<SVGElement, SVGElementEventMap> &
37
+ UseEventListenerWithExplicitTarget<MathMLElement, MathMLElementEventMap>;
38
+
37
39
  /**
38
40
  * @see
39
41
  * {@linkcode useEventListener},
@@ -149,7 +151,9 @@ export const useEventListener: UseEventListener = function useEventListener(
149
151
  target && !('addEventListener' in target) ? target.current : target;
150
152
 
151
153
  const handlerRef = useRef(handler);
152
- handlerRef.current = handler;
154
+ useEffect(() => {
155
+ handlerRef.current = handler;
156
+ }, [handler]);
153
157
 
154
158
  const {
155
159
  capture = false,
@@ -11,54 +11,10 @@ import type { useReducerWithDeps } from './useReducerWithDeps.js';
11
11
  * This hook is designed in the most general way possible in order to cover all
12
12
  * imaginable use cases.
13
13
  *
14
- * @example
15
- * Sometimes, React's immutability constraints mean too much unnecessary copying
16
- * of data when new data arrives at a high frequency. In such cases, it might be
17
- * desirable to ignore the constraints by embracing imperative patterns.
18
- * Here is an example of a scenario where that can make sense:
19
- *
20
- * ```tsx
21
- * type SensorData = { timestamp: number; value: number };
22
- * const sensorDataRef = useRef<SensorData[]>([]);
23
- * const mostRecentSensorDataTimestampRef = useRef<number>(0);
24
- *
25
- * const [forceUpdate, updateCount] = useForceUpdate();
26
- * // Limiting the frequency of forced re-renders with some throttle function:
27
- * const throttledForceUpdateRef = useRef(throttle(forceUpdate));
28
- *
29
- * useEffect(() => {
30
- * return sensorDataObservable.subscribe((data: SensorData) => {
31
- * // Imagine new sensor data arrives every 1 millisecond. If we were following
32
- * // React's immutability rules by creating a new array every time, the data
33
- * // that's already there would have to be copied many times before the new
34
- * // data would even get a chance to be reflected in the UI for the first time
35
- * // because it typically takes much longer than 1 millisecond for a new frame
36
- * // to be displayed. To prevent the waste of computational resources, we just
37
- * // mutate the existing array every time instead:
38
- * sensorDataRef.current.push(data);
39
- * if (data.timestamp > mostRecentSensorDataTimestampRef.current) {
40
- * mostRecentSensorDataTimestampRef.current = data.timestamp;
41
- * }
42
- * throttledForceUpdateRef.current();
43
- * });
44
- * }, []);
45
- *
46
- * const [timeWindow, setTimeWindow] = useState(1000);
47
- * const selectedSensorData = useMemo(
48
- * () => {
49
- * // Keep this line if you don't want to disable the
50
- * // react-hooks/exhaustive-deps ESLint rule:
51
- * updateCount;
52
- * const threshold = mostRecentSensorDataTimestampRef.current - timeWindow;
53
- * return sensorDataRef.current.filter(
54
- * ({ timestamp }) => timestamp >= threshold,
55
- * );
56
- * },
57
- * // sensorDataRef.current always references the same array, so listing it as a
58
- * // dependency is pointless. Instead, updateCount should be used:
59
- * [updateCount, timeWindow],
60
- * );
61
- * ```
14
+ * @deprecated
15
+ * This hook encourages patterns that are unsafe in Concurrent React.
16
+ * For details and ideas on how to get rid of it, please check the discussion at
17
+ * https://www.reddit.com/r/react/comments/1nqcsri/comment/ng76cv5/.
62
18
  *
63
19
  * @param callback An optional callback function to call during renders that
64
20
  * were triggered with `forceUpdate()`
@@ -18,6 +18,13 @@ export type ActionDispatch<ActionArg extends AnyActionArg> = (
18
18
  * `useReducer` hook with an additional dependency array `deps` that resets the
19
19
  * state to `initialState` when dependencies change
20
20
  *
21
+ * This hook is the reducer pattern counterpart of {@linkcode useStateWithDeps}.
22
+ *
23
+ * Due to React's limitations, a change in dependencies always causes two
24
+ * renders when using this hook. The result of the first render is thrown away
25
+ * as described in
26
+ * [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
27
+ *
21
28
  * For motivation and examples, see
22
29
  * https://github.com/facebook/react/issues/33041.
23
30
  *
@@ -60,9 +67,9 @@ export function useReducerWithDeps<S, A extends AnyActionArg>(
60
67
  // Only the initially provided reducer is used
61
68
  const reducerRef = useRef(reducer);
62
69
 
63
- const dispatch = useRef(function dispatch(...args: A): void {
70
+ function dispatch(...args: A): void {
64
71
  setState((previousState) => reducerRef.current(previousState, ...args));
65
- }).current;
72
+ }
66
73
 
67
- return [state, dispatch];
74
+ return [state, useRef(dispatch).current];
68
75
  }
@@ -1,23 +1,20 @@
1
- /**
2
- * @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Peter Juras
6
- */
7
-
8
1
  import {
9
- useRef,
2
+ useState,
10
3
  type DependencyList,
11
4
  type Dispatch,
12
5
  type SetStateAction,
13
6
  } from 'react';
14
- import { depsAreEqual, isFunction } from '../utils.js';
15
- import { useForceUpdate } from './useForceUpdate.js';
7
+ import { depsAreEqual } from '../utils.js';
16
8
 
17
9
  /**
18
10
  * `useState` hook with an additional dependency array `deps` that resets the
19
11
  * state to `initialState` when dependencies change
20
12
  *
13
+ * Due to React's limitations, a change in dependencies always causes two
14
+ * renders when using this hook. The result of the first render is thrown away
15
+ * as described in
16
+ * [useState > Storing information from previous renders](https://react.dev/reference/react/useState#storing-information-from-previous-renders).
17
+ *
21
18
  * For motivation and more examples, see
22
19
  * https://github.com/facebook/react/issues/33041.
23
20
  *
@@ -36,7 +33,7 @@ import { useForceUpdate } from './useForceUpdate.js';
36
33
  * evening: ['board games', 'dinner'],
37
34
  * };
38
35
  *
39
- * export function Example() {
36
+ * function Example() {
40
37
  * const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
41
38
  *
42
39
  * const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
@@ -66,45 +63,14 @@ export function useStateWithDeps<S>(
66
63
  initialState: S | ((previousState?: S) => S),
67
64
  deps: DependencyList,
68
65
  ): [S, Dispatch<SetStateAction<S>>] {
69
- // It would be possible to use useState instead of useRef to store the state,
70
- // however this would trigger re-renders whenever the state is reset due to a
71
- // change in dependencies. In order to avoid these re-renders, the state is
72
- // stored in a ref, and updates are triggered with forceUpdate when necessary.
73
- const state = useRef(undefined as S);
66
+ const [state, setState] = useState(initialState);
74
67
 
75
- const prevDeps = useRef(deps);
76
- const isMounted = useRef(false);
68
+ const [prevDeps, setPrevDeps] = useState(deps);
77
69
 
78
- // If first render, or if dependencies have changed since last time
79
- if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
80
- // Update state and deps
81
- let nextState: S;
82
- if (isFunction(initialState)) {
83
- nextState = initialState(state.current);
84
- } else {
85
- nextState = initialState;
86
- }
87
- state.current = nextState;
88
- prevDeps.current = deps;
89
- isMounted.current = true;
70
+ if (!depsAreEqual(deps, prevDeps)) {
71
+ setPrevDeps(deps);
72
+ setState(initialState);
90
73
  }
91
74
 
92
- const [forceUpdate] = useForceUpdate();
93
-
94
- const updateState = useRef(function updateState(
95
- newState: S | ((previousState: S) => S),
96
- ): void {
97
- let nextState: S;
98
- if (isFunction(newState)) {
99
- nextState = newState(state.current);
100
- } else {
101
- nextState = newState;
102
- }
103
- if (!Object.is(state.current, nextState)) {
104
- state.current = nextState;
105
- forceUpdate();
106
- }
107
- }).current;
108
-
109
- return [state.current, updateState];
75
+ return [state, setState];
110
76
  }
package/src/utils.ts CHANGED
@@ -1,19 +1,11 @@
1
1
  import type { DependencyList } from 'react';
2
2
 
3
- export type Callable = (...args: never) => unknown;
4
-
5
3
  export type ArgumentFallback<
6
4
  T extends Base,
7
5
  Default extends Base,
8
6
  Base = unknown,
9
7
  > = [T] extends [never] ? Default : [Base] extends [T] ? Default : T;
10
8
 
11
- export function noop() {}
12
-
13
- export function isFunction(input: unknown): input is Callable {
14
- return typeof input === 'function';
15
- }
16
-
17
9
  export function depsAreEqual(
18
10
  prevDeps: DependencyList,
19
11
  deps: DependencyList,