@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,91 @@
1
+ import { useReducer, useRef } from 'react';
2
+
3
+ /* eslint-disable */
4
+ import type { useStateWithDeps } from './useStateWithDeps.js';
5
+ import type { useReducerWithDeps } from './useReducerWithDeps.js';
6
+ /* eslint-enable */
7
+
8
+ /**
9
+ * Enables you to imperatively trigger re-rendering of components
10
+ *
11
+ * This hook is designed in the most general way possible in order to cover all
12
+ * imaginable use cases.
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
+ * ```
62
+ *
63
+ * @param callback An optional callback function to call during renders that
64
+ * were triggered with `forceUpdate()`
65
+ *
66
+ * Can be used for conditionally calling state setters when state needs to be
67
+ * reset. That is legal and better than using effects (see
68
+ * {@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}),
69
+ * but can often be avoided by using {@linkcode useStateWithDeps} or
70
+ * {@linkcode useReducerWithDeps}.
71
+ *
72
+ * Important: the callback function is called once per render, not once per
73
+ * `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
74
+ * be called once.
75
+ *
76
+ * @returns An array with the following two elements:
77
+ *
78
+ * 1. A `forceUpdate` function that triggers a re-render
79
+ * 2. The number of times `forceUpdate` has been called so far
80
+ */
81
+ export function useForceUpdate(callback?: () => void): [() => void, bigint] {
82
+ // It is very unlikely that the number of updates will exceed
83
+ // Number.MAX_SAFE_INTEGER, but not impossible. That is why we use bigints.
84
+ const [updateCount, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
85
+ const updateCountRef = useRef(updateCount);
86
+ if (updateCount !== updateCountRef.current) {
87
+ updateCountRef.current = updateCount;
88
+ callback?.();
89
+ }
90
+ return [forceUpdate, updateCount];
91
+ }
@@ -1,16 +1,25 @@
1
- import {
2
- type ActionDispatch,
3
- type AnyActionArg,
4
- type DependencyList,
5
- useCallback,
6
- useRef,
7
- } from 'react';
8
- import useStateWithDeps from './useStateWithDeps.js';
1
+ import { type DependencyList, useCallback, useRef } from 'react';
2
+ import { useStateWithDeps } from './useStateWithDeps.js';
3
+
4
+ // We cannot simply import the following types from @types/react since they are
5
+ // only available starting from React 19, but we also want to support React 18
6
+ // whose type declarations for useReducer are very different.
7
+
8
+ /** @ignore */
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ export type AnyActionArg = [] | [any];
11
+
12
+ /** @ignore */
13
+ export type ActionDispatch<ActionArg extends AnyActionArg> = (
14
+ ...args: ActionArg
15
+ ) => void;
9
16
 
10
17
  /**
11
- * `useReducer` hook with an additional dependency array that resets the state
12
- * to the `initialState` param when the dependencies passed in the `deps` array
13
- * change
18
+ * `useReducer` hook with an additional dependency array `deps` that resets the
19
+ * state to `initialState` when dependencies change
20
+ *
21
+ * For motivation and examples, see
22
+ * https://github.com/facebook/react/issues/33041.
14
23
  *
15
24
  * ### On linter support
16
25
  *
@@ -19,28 +28,28 @@ import useStateWithDeps from './useStateWithDeps.js';
19
28
  * However, as we would like to keep the hook as compatible with `useReducer` as
20
29
  * possible, we don't want to artificially change the parameter's position.
21
30
  * Therefore, there will be no warnings about missing dependencies.
22
- * Because of that, addition caution is advised!
23
- * Be sure to check no dependencies are missing from the `deps` array.
31
+ * Because of that, additional caution is advised!
32
+ * Be sure to check that no dependencies are missing from the `deps` array.
24
33
  *
25
34
  * Related issue: {@link https://github.com/facebook/react/issues/25443}.
26
35
  *
27
36
  * Unlike `eslint-plugin-react-hooks` maintained by React's team, the unofficial
28
37
  * `useExhaustiveDependencies` rule provided for Biome by Biome's team
29
38
  * does actually have support for dependency arrays at other positions, see
30
- * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies}.
39
+ * {@link https://biomejs.dev/linter/rules/use-exhaustive-dependencies/#validating-dependencies useExhaustiveDependencies > Options > Validating dependencies}.
31
40
  *
32
41
  * @param reducer The reducer function that specifies how the state gets updated
33
42
  *
34
- * @param initialState The state that will be set when the component mounts or
35
- * the dependencies change
43
+ * @param initialState The value to which the state is set when the component is
44
+ * mounted or dependencies change
36
45
  *
37
- * It can also be a function which returns a state value. If the state is reset
46
+ * It can also be a function that returns a state value. If the state is reset
38
47
  * due to a change of dependencies, this function will be passed the previous
39
48
  * state as its argument (will be `undefined` in the first call upon mount).
40
49
  *
41
50
  * @param deps Dependencies that reset the state to `initialState`
42
51
  */
43
- export default function useReducerWithDeps<S, A extends AnyActionArg>(
52
+ export function useReducerWithDeps<S, A extends AnyActionArg>(
44
53
  reducer: (prevState: S, ...args: A) => S,
45
54
  initialState: S | ((previousState?: S) => S),
46
55
  deps: DependencyList,
@@ -0,0 +1,111 @@
1
+ /**
2
+ * @file Based on {@link https://github.com/peterjuras/use-state-with-deps}
3
+ *
4
+ * @license MIT
5
+ * @copyright 2020 Peter Juras
6
+ */
7
+
8
+ import {
9
+ useCallback,
10
+ useRef,
11
+ type DependencyList,
12
+ type Dispatch,
13
+ type SetStateAction,
14
+ } from 'react';
15
+ import { depsAreEqual, isFunction } from '../utils.js';
16
+ import { useForceUpdate } from './useForceUpdate.js';
17
+
18
+ /**
19
+ * `useState` hook with an additional dependency array `deps` that resets the
20
+ * state to `initialState` when dependencies change
21
+ *
22
+ * For motivation and more examples, see
23
+ * https://github.com/facebook/react/issues/33041.
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * type Activity = 'breakfast' | 'exercise' | 'swim' | 'board games' | 'dinner';
28
+ *
29
+ * const timeOfDayOptions = ['morning', 'afternoon', 'evening'] as const;
30
+ * type TimeOfDay = (typeof timeOfDayOptions)[number];
31
+ *
32
+ * const activityOptionsByTimeOfDay: {
33
+ * [K in TimeOfDay]: [Activity, ...Activity[]];
34
+ * } = {
35
+ * morning: ['breakfast', 'exercise', 'swim'],
36
+ * afternoon: ['exercise', 'swim', 'board games'],
37
+ * evening: ['board games', 'dinner'],
38
+ * };
39
+ *
40
+ * export function Example() {
41
+ * const [timeOfDay, setTimeOfDay] = useState<TimeOfDay>('morning');
42
+ *
43
+ * const activityOptions = activityOptionsByTimeOfDay[timeOfDay];
44
+ * const [activity, setActivity] = useStateWithDeps<Activity>(
45
+ * (prev) => {
46
+ * // Make sure activity is always valid for the current timeOfDay value,
47
+ * // but also don't reset it unless necessary:
48
+ * return prev && activityOptions.includes(prev) ? prev : activityOptions[0];
49
+ * },
50
+ * [activityOptions],
51
+ * );
52
+ *
53
+ * return '...';
54
+ * }
55
+ * ```
56
+ *
57
+ * @param initialState The value to which the state is set when the component is
58
+ * mounted or dependencies change
59
+ *
60
+ * It can also be a function that returns a state value. If the state is reset
61
+ * due to a change of dependencies, this function will be passed the previous
62
+ * state as its argument (will be `undefined` in the first call upon mount).
63
+ *
64
+ * @param deps Dependencies that reset the state to `initialState`
65
+ */
66
+ export function useStateWithDeps<S>(
67
+ initialState: S | ((previousState?: S) => S),
68
+ deps: DependencyList,
69
+ ): [S, Dispatch<SetStateAction<S>>] {
70
+ // It would be possible to use useState instead of useRef to store the state,
71
+ // however this would trigger re-renders whenever the state is reset due to a
72
+ // change in dependencies. In order to avoid these re-renders, the state is
73
+ // stored in a ref, and updates are triggered with forceUpdate when necessary.
74
+ const state = useRef(undefined as S);
75
+
76
+ const prevDeps = useRef(deps);
77
+ const isMounted = useRef(false);
78
+
79
+ // If first render, or if dependencies have changed since last time
80
+ if (!isMounted.current || !depsAreEqual(prevDeps.current, deps)) {
81
+ // Update state and deps
82
+ let nextState: S;
83
+ if (isFunction(initialState)) {
84
+ nextState = initialState(state.current);
85
+ } else {
86
+ nextState = initialState;
87
+ }
88
+ state.current = nextState;
89
+ prevDeps.current = deps;
90
+ isMounted.current = true;
91
+ }
92
+
93
+ const [forceUpdate] = useForceUpdate();
94
+
95
+ const updateState = useCallback(function updateState(
96
+ newState: S | ((previousState: S) => S),
97
+ ): void {
98
+ let nextState: S;
99
+ if (isFunction(newState)) {
100
+ nextState = newState(state.current);
101
+ } else {
102
+ nextState = newState;
103
+ }
104
+ if (!Object.is(state.current, nextState)) {
105
+ state.current = nextState;
106
+ forceUpdate();
107
+ }
108
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
109
+
110
+ return [state.current, updateState];
111
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from './hooks/index.js';
2
+ export * from './misc/index.js';
@@ -0,0 +1,116 @@
1
+ import { type Context, type Provider, createContext, useContext } from 'react';
2
+ import type { ArgumentFallback } from '../utils.js';
3
+
4
+ const moValueSymbol = Symbol('noValue');
5
+
6
+ /**
7
+ * A React context with a required `displayName` and the obsolete `Consumer`
8
+ * property purposefully omitted so that it is impossible to pass the context
9
+ * as an argument to `useContext` or `use` (the hook produced with
10
+ * {@linkcode createSafeContext} should be used instead)
11
+ *
12
+ * @see {@linkcode createSafeContext}
13
+ */
14
+ // The type is conditional so that both React 18 and 19 are correctly supported.
15
+ // The code duplication is necessary for the type to be displayed correctly by
16
+ // TypeDoc.
17
+ export type RestrictedContext<T> =
18
+ Context<T> extends Provider<T>
19
+ ? { Provider: Provider<T>; displayName: string } & Provider<T>
20
+ : { Provider: Provider<T>; displayName: string };
21
+
22
+ /**
23
+ * The return type of {@linkcode createSafeContext}
24
+ *
25
+ * @see {@linkcode createSafeContext}
26
+ */
27
+ export type SafeContext<DisplayName extends string, T> = {
28
+ [K in `${DisplayName}Context`]: RestrictedContext<T>;
29
+ } & {
30
+ [K in `use${DisplayName}`]: () => T;
31
+ };
32
+
33
+ /**
34
+ * For a given type `T`, returns a function that produces both a context of that
35
+ * type and a hook that returns the current context value if one was provided,
36
+ * or throws an error otherwise
37
+ *
38
+ * The advantages over vanilla `createContext` are that no default value has to
39
+ * be provided, and that a meaningful context name is displayed in dev tools
40
+ * instead of generic `Context.Provider`.
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * enum Direction {
45
+ * Up,
46
+ * Down,
47
+ * Left,
48
+ * Right,
49
+ * }
50
+ *
51
+ * // Before
52
+ * const DirectionContext = createContext<Direction | undefined>(undefined);
53
+ * DirectionContext.displayName = 'DirectionContext';
54
+ *
55
+ * const useDirection = () => {
56
+ * const direction = useContext(DirectionContext);
57
+ * if (direction === undefined) {
58
+ * // Called outside of a <DirectionContext.Provider> boundary!
59
+ * // Or maybe undefined was explicitly provided as the context value
60
+ * // (ideally that shouldn't be allowed, but it is because we had to include
61
+ * // undefined in the context type so as to provide a meaningful default)
62
+ * throw new Error('No DirectionContext value was provided');
63
+ * }
64
+ * // Thanks to the undefined check, the type is now narrowed down to Direction
65
+ * return direction;
66
+ * };
67
+ *
68
+ * // After
69
+ * const { DirectionContext, useDirection } =
70
+ * createSafeContext<Direction>()('Direction'); // That's it :)
71
+ *
72
+ * const Parent = () => (
73
+ * // Providing undefined as the value is not allowed 👍
74
+ * <Direction.Provider value={Direction.Up}>
75
+ * <Child />
76
+ * </Direction.Provider>
77
+ * );
78
+ *
79
+ * const Child = () => `Current direction: ${Direction[useDirection()]}`;
80
+ * ```
81
+ *
82
+ * @returns
83
+ * A function that accepts a single string argument `displayName` (e.g.
84
+ * `"Direction"`) and returns an object with the following properties:
85
+ * - ``` `${displayName}Context` ``` (e.g. `DirectionContext`): the context
86
+ * - ``` `use${displayName}` ``` (e.g. `useDirection`): a hook that returns the
87
+ * current context value if one was provided, or throws an error otherwise
88
+ */
89
+ export function createSafeContext<T = never>() {
90
+ return <DisplayName extends string>(
91
+ displayName: [T] extends [never]
92
+ ? never
93
+ : ArgumentFallback<DisplayName, never, string>,
94
+ ): SafeContext<DisplayName, T> => {
95
+ const contextName = `${displayName as DisplayName}Context` as const;
96
+ const hookName = `use${displayName as DisplayName}` as const;
97
+
98
+ const Context = createContext<T | typeof moValueSymbol>(moValueSymbol);
99
+ Context.displayName = contextName;
100
+
101
+ return {
102
+ [contextName]: Context as RestrictedContext<T>,
103
+ [hookName]: () => {
104
+ const value = useContext(Context);
105
+ if (value === moValueSymbol) {
106
+ throw new Error(`No ${contextName} value was provided`);
107
+ }
108
+ return value;
109
+ },
110
+ } as {
111
+ [K in typeof contextName]: RestrictedContext<T>;
112
+ } & {
113
+ [K in typeof hookName]: () => T;
114
+ };
115
+ };
116
+ }
@@ -0,0 +1 @@
1
+ export * from './createSafeContext.js';
@@ -1,9 +1,16 @@
1
1
  import type { DependencyList } from 'react';
2
2
 
3
+ export type Callable = (...args: never) => unknown;
4
+
5
+ export type ArgumentFallback<
6
+ T extends Base,
7
+ Default extends Base,
8
+ Base = unknown,
9
+ > = [T] extends [never] ? Default : [Base] extends [T] ? Default : T;
10
+
3
11
  export function noop() {}
4
12
 
5
- // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
6
- export function isFunction(input: unknown): input is Function {
13
+ export function isFunction(input: unknown): input is Callable {
7
14
  return typeof input === 'function';
8
15
  }
9
16
 
@@ -1,4 +0,0 @@
1
- import { useLayoutEffect } from 'react';
2
- declare const useIsomorphicLayoutEffect: typeof useLayoutEffect;
3
- export default useIsomorphicLayoutEffect;
4
- //# sourceMappingURL=useIsomorphicLayoutEffect.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useIsomorphicLayoutEffect.d.ts","sourceRoot":"","sources":["../../lib/hooks/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AAGxC,QAAA,MAAM,yBAAyB,wBACyB,CAAC;AAEzD,eAAe,yBAAyB,CAAC"}
@@ -1,5 +0,0 @@
1
- import { useLayoutEffect } from 'react';
2
- import { noop } from "../utils/index.js";
3
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : noop;
4
- export default useIsomorphicLayoutEffect;
5
- //# sourceMappingURL=useIsomorphicLayoutEffect.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useIsomorphicLayoutEffect.js","sourceRoot":"","sources":["../../lib/hooks/useIsomorphicLayoutEffect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,MAAM,yBAAyB,GAC7B,OAAO,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC;AAEzD,eAAe,yBAAyB,CAAC"}
@@ -1,5 +0,0 @@
1
- import type { DependencyList } from 'react';
2
- export declare function noop(): void;
3
- export declare function isFunction(input: unknown): input is Function;
4
- export declare function depsAreEqual(prevDeps: DependencyList, deps: DependencyList): boolean;
5
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,wBAAgB,IAAI,SAAK;AAGzB,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 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../lib/utils/index.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,IAAI,KAAI,CAAC;AAEzB,sEAAsE;AACtE,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,OAAO,OAAO,KAAK,KAAK,UAAU,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,QAAwB,EACxB,IAAoB;IAEpB,OAAO,CACL,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM;QAC/B,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAC5D,CAAC;AACJ,CAAC"}
@@ -1,101 +0,0 @@
1
- /**
2
- * @file Based on {@link https://github.com/juliencrn/usehooks-ts}
3
- *
4
- * @license MIT
5
- * @copyright 2020 Julien CARON
6
- */
7
-
8
- import { useEffect, useRef } from 'react';
9
- import useIsomorphicLayoutEffect from './useIsomorphicLayoutEffect.ts';
10
-
11
- /**
12
- * Adds `handler` as a listener for the event `eventName` of `element`
13
- * (or `window` by default) with the provided `options` applied
14
- *
15
- * It is the user's responsibility to make sure `element` and `options` values
16
- * are correctly memoized!
17
- */
18
-
19
- // SVGElement Event based useEventListener interface
20
- function useEventListener<
21
- K extends keyof SVGElementEventMap,
22
- T extends SVGElement,
23
- >(
24
- eventName: K,
25
- handler: (this: T, event: SVGElementEventMap[K]) => void,
26
- element: T | null,
27
- options?: boolean | AddEventListenerOptions,
28
- ): void;
29
-
30
- // HTMLElement Event based useEventListener interface
31
- function useEventListener<
32
- K extends keyof HTMLElementEventMap,
33
- T extends HTMLElement,
34
- >(
35
- eventName: K,
36
- handler: (this: T, event: HTMLElementEventMap[K]) => void,
37
- element: T | null,
38
- options?: boolean | AddEventListenerOptions,
39
- ): void;
40
-
41
- // Document Event based useEventListener interface
42
- function useEventListener<K extends keyof DocumentEventMap>(
43
- eventName: K,
44
- handler: (this: Document, event: DocumentEventMap[K]) => void,
45
- element: Document,
46
- options?: boolean | AddEventListenerOptions,
47
- ): void;
48
-
49
- // Window Event based useEventListener interface
50
- function useEventListener<K extends keyof WindowEventMap>(
51
- eventName: K,
52
- handler: (this: Window, event: WindowEventMap[K]) => void,
53
- element?: Window,
54
- options?: boolean | AddEventListenerOptions,
55
- ): void;
56
-
57
- // Fallback overload for all other event targets and types
58
- function useEventListener<T extends EventTarget>(
59
- eventName: string,
60
- handler: (this: T, event: Event) => void,
61
- element?: T | null,
62
- options?: boolean | AddEventListenerOptions,
63
- ): void;
64
-
65
- function useEventListener(
66
- eventName: string,
67
- handler: (this: EventTarget, event: Event) => void,
68
- element?: EventTarget | null,
69
- options?: boolean | AddEventListenerOptions,
70
- ) {
71
- // Create a ref that stores handler
72
- const savedHandler = useRef(handler);
73
-
74
- useIsomorphicLayoutEffect(() => {
75
- savedHandler.current = handler;
76
- }, [handler]);
77
-
78
- useEffect(() => {
79
- if (element === null) {
80
- // No element has been attached to the ref yet
81
- return;
82
- }
83
-
84
- // Define the listening target
85
- const targetElement = element ?? window;
86
-
87
- // Create event listener that calls handler function stored in ref
88
- const listener: typeof handler = function (event) {
89
- savedHandler.current.call(this, event);
90
- };
91
-
92
- targetElement.addEventListener(eventName, listener, options);
93
-
94
- // Remove event listener on cleanup
95
- return () => {
96
- targetElement.removeEventListener(eventName, listener, options);
97
- };
98
- }, [eventName, element, options]);
99
- }
100
-
101
- export default useEventListener;
@@ -1,40 +0,0 @@
1
- import { useReducer, useRef } from 'react';
2
-
3
- /**
4
- * Enables you to imperatively trigger re-rendering of components
5
- *
6
- * This hook is designed in the most general way possible in order to cover all
7
- * imaginable use cases.
8
- *
9
- * @param callback An optional callback function to call during renders that
10
- * were triggered with `forceUpdate()`
11
- *
12
- * Can be used for conditionally calling state setters when state needs to be
13
- * reset. That is legal and better than using effects (see
14
- * {@link https://react.dev/learn/-might-not-need-an-effect#adjusting-some-state-when-a-prop-changes You Might Not Need an Effect > Adjusting some state when a prop changes}),
15
- * but can often be avoided by using
16
- * [`useStateWithDeps`]({@link ./useStateWithDeps.ts}) or
17
- * [`useReducerWithDeps`]({@link ./useReducerWithDeps.ts}).
18
- *
19
- * Important: the callback function is called once per render, not once per
20
- * `forceUpdate` call! If React batches `forceUpdate` calls, then it will only
21
- * be called once.
22
- *
23
- * @returns An array with the following two elements:
24
- *
25
- * 1. A `forceUpdate` function that triggers a re-render
26
- * 2. The number of times `forceUpdate` has been called so far
27
- */
28
- export default function useForceUpdate(
29
- callback?: () => void,
30
- ): [() => void, bigint] {
31
- // It is very unlikely that the number of updates will exceed
32
- // Number.MAX_SAFE_INTEGER, but not impossible. That is why we use bigints.
33
- const [counter, forceUpdate] = useReducer((prev) => prev + 1n, 0n);
34
- const counterRef = useRef(counter);
35
- if (counter !== counterRef.current) {
36
- counterRef.current = counter;
37
- callback?.();
38
- }
39
- return [forceUpdate, counter];
40
- }
@@ -1,7 +0,0 @@
1
- import { useLayoutEffect } from 'react';
2
- import { noop } from '../utils/index.ts';
3
-
4
- const useIsomorphicLayoutEffect =
5
- typeof window !== 'undefined' ? useLayoutEffect : noop;
6
-
7
- export default useIsomorphicLayoutEffect;