@reactuses/core 6.1.9 → 6.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -23,7 +23,7 @@ function isString(val) {
23
23
  const isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';
24
24
  const isBrowser = typeof window !== 'undefined';
25
25
  const isNavigator = typeof navigator !== 'undefined';
26
- function noop() {}
26
+ function noop$1() {}
27
27
  const isIOS = isBrowser && ((_window = window) == null ? void 0 : (_window_navigator = _window.navigator) == null ? void 0 : _window_navigator.userAgent) && /iP(?:ad|hone|od)/.test(window.navigator.userAgent);
28
28
 
29
29
  const useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect;
@@ -113,10 +113,57 @@ const useDeepCompareEffect = (effect, deps)=>{
113
113
  useCustomCompareEffect(effect, deps, lodashEs.isEqual);
114
114
  };
115
115
 
116
- function useEventListener(eventName, handler, element, options = defaultOptions$1) {
116
+ /**
117
+ * Creates a stable identifier for a BasicTarget that can be safely used in effect dependencies.
118
+ *
119
+ * This hook solves the problem where passing unstable function references like `() => document`
120
+ * would cause infinite re-renders when used directly in effect dependency arrays.
121
+ *
122
+ * @param target - The target element (ref, function, or direct element)
123
+ * @param defaultElement - Default element to use if target is undefined
124
+ * @returns A stable reference that only changes when the actual target element changes
125
+ *
126
+ * @example
127
+ * ```tsx
128
+ * // For ref objects: returns the ref itself (stable)
129
+ * const ref = useRef<HTMLDivElement>(null)
130
+ * const key = useStableTarget(ref) // key === ref (stable)
131
+ *
132
+ * // For functions: returns the resolved actual element
133
+ * const key = useStableTarget(() => document) // key === document (stable)
134
+ *
135
+ * // For direct elements: returns the element itself
136
+ * const key = useStableTarget(divElement) // key === divElement (stable)
137
+ * ```
138
+ */ function useStableTarget(target, defaultElement) {
139
+ const targetRef = React.useRef(target);
140
+ targetRef.current = target;
141
+ // Calculate stable key without memoization
142
+ // For ref objects: return the ref itself (always stable)
143
+ // For functions/direct elements: resolve to the actual element
144
+ let stableKey;
145
+ if (!target) {
146
+ stableKey = defaultElement != null ? defaultElement : null;
147
+ } else if (typeof target === 'object' && 'current' in target) {
148
+ // Ref object - use the ref itself as the stable key
149
+ stableKey = target;
150
+ } else {
151
+ // Function or direct element - resolve to actual element
152
+ stableKey = getTargetElement(target, defaultElement);
153
+ }
154
+ return {
155
+ /** The stable key that can be safely used in effect dependencies */ key: stableKey,
156
+ /** A ref containing the current target (useful for accessing in effects) */ ref: targetRef
157
+ };
158
+ }
159
+
160
+ function useEventListenerImpl(eventName, handler, element, options = defaultOptions$1) {
117
161
  const savedHandler = useLatest(handler);
162
+ const { key: elementKey, ref: elementRef } = useStableTarget(element, defaultWindow);
118
163
  useDeepCompareEffect(()=>{
119
- const targetElement = getTargetElement(element, defaultWindow);
164
+ // Call getTargetElement inside effect to support ref-based targets
165
+ // (ref.current is null during render, only available in commit phase)
166
+ const targetElement = getTargetElement(elementRef.current, defaultWindow);
120
167
  if (!(targetElement && targetElement.addEventListener)) {
121
168
  return;
122
169
  }
@@ -130,10 +177,12 @@ function useEventListener(eventName, handler, element, options = defaultOptions$
130
177
  };
131
178
  }, [
132
179
  eventName,
133
- element,
180
+ elementKey,
134
181
  options
135
182
  ]);
136
183
  }
184
+ function noop() {}
185
+ const useEventListener = isBrowser ? useEventListenerImpl : noop;
137
186
 
138
187
  const useMount = (fn)=>{
139
188
  if (isDev) {
@@ -203,7 +252,7 @@ function _async_to_generator$7(fn) {
203
252
  });
204
253
  };
205
254
  }
206
- const useAsyncEffect = (effect, cleanup = noop, deps)=>{
255
+ const useAsyncEffect = (effect, cleanup = noop$1, deps)=>{
207
256
  const mounted = useMountedState();
208
257
  React.useEffect(()=>{
209
258
  const execute = ()=>_async_to_generator$7(function*() {
@@ -1097,13 +1146,14 @@ const useDropZone = (target, onDrop)=>{
1097
1146
  const useResizeObserver = (target, callback, options = defaultOptions$1)=>{
1098
1147
  const savedCallback = useLatest(callback);
1099
1148
  const observerRef = React.useRef();
1149
+ const { key: targetKey, ref: targetRef } = useStableTarget(target);
1100
1150
  const stop = React.useCallback(()=>{
1101
1151
  if (observerRef.current) {
1102
1152
  observerRef.current.disconnect();
1103
1153
  }
1104
1154
  }, []);
1105
1155
  useDeepCompareEffect(()=>{
1106
- const element = getTargetElement(target);
1156
+ const element = getTargetElement(targetRef.current);
1107
1157
  if (!element) {
1108
1158
  return;
1109
1159
  }
@@ -1111,9 +1161,7 @@ const useResizeObserver = (target, callback, options = defaultOptions$1)=>{
1111
1161
  observerRef.current.observe(element, options);
1112
1162
  return stop;
1113
1163
  }, [
1114
- savedCallback,
1115
- stop,
1116
- target,
1164
+ targetKey,
1117
1165
  options
1118
1166
  ]);
1119
1167
  return stop;
@@ -1224,13 +1272,14 @@ const useElementSize = (target, options = defaultOptions$1)=>{
1224
1272
  const useIntersectionObserver = (target, callback, options = defaultOptions$1)=>{
1225
1273
  const savedCallback = useLatest(callback);
1226
1274
  const observerRef = React.useRef();
1275
+ const { key: targetKey, ref: targetRef } = useStableTarget(target);
1227
1276
  const stop = React.useCallback(()=>{
1228
1277
  if (observerRef.current) {
1229
1278
  observerRef.current.disconnect();
1230
1279
  }
1231
1280
  }, []);
1232
1281
  useDeepCompareEffect(()=>{
1233
- const element = getTargetElement(target);
1282
+ const element = getTargetElement(targetRef.current);
1234
1283
  if (!element) {
1235
1284
  return;
1236
1285
  }
@@ -1238,6 +1287,7 @@ const useIntersectionObserver = (target, callback, options = defaultOptions$1)=>
1238
1287
  observerRef.current.observe(element);
1239
1288
  return stop;
1240
1289
  }, [
1290
+ targetKey,
1241
1291
  options
1242
1292
  ]);
1243
1293
  return stop;
@@ -1774,7 +1824,7 @@ const defaultListerOptions = {
1774
1824
  passive: true
1775
1825
  };
1776
1826
  const useScroll = (target, options = defaultOptions$1)=>{
1777
- const { throttle = 0, idle = 200, onStop = noop, onScroll = noop, offset = {
1827
+ const { throttle = 0, idle = 200, onStop = noop$1, onScroll = noop$1, offset = {
1778
1828
  left: 0,
1779
1829
  right: 0,
1780
1830
  top: 0,
@@ -2112,7 +2162,7 @@ const useMediaDevices = (options = {})=>{
2112
2162
  label
2113
2163
  }))
2114
2164
  });
2115
- }).catch(noop);
2165
+ }).catch(noop$1);
2116
2166
  }, []);
2117
2167
  const ensurePermissions = React.useCallback(()=>_async_to_generator$3(function*() {
2118
2168
  if (!isSupported) {
@@ -2343,13 +2393,14 @@ const useMousePressed = (target, options = defaultOptions$1)=>{
2343
2393
  const useMutationObserver = (callback, target, options = defaultOptions$1)=>{
2344
2394
  const callbackRef = useLatest(callback);
2345
2395
  const observerRef = React.useRef();
2396
+ const { key: targetKey, ref: targetRef } = useStableTarget(target);
2346
2397
  const stop = React.useCallback(()=>{
2347
2398
  if (observerRef.current) {
2348
2399
  observerRef.current.disconnect();
2349
2400
  }
2350
2401
  }, []);
2351
2402
  useDeepCompareEffect(()=>{
2352
- const element = getTargetElement(target);
2403
+ const element = getTargetElement(targetRef.current);
2353
2404
  if (!element) {
2354
2405
  return;
2355
2406
  }
@@ -2357,6 +2408,7 @@ const useMutationObserver = (callback, target, options = defaultOptions$1)=>{
2357
2408
  observerRef.current.observe(element, options);
2358
2409
  return stop;
2359
2410
  }, [
2411
+ targetKey,
2360
2412
  options
2361
2413
  ]);
2362
2414
  return stop;
@@ -2515,13 +2567,13 @@ function usePageLeave() {
2515
2567
  const from = event.relatedTarget || event.toElement;
2516
2568
  setIsLeft(!from);
2517
2569
  };
2518
- useEventListener('mouseout', handler, ()=>window, {
2570
+ useEventListener('mouseout', handler, defaultWindow, {
2519
2571
  passive: true
2520
2572
  });
2521
- useEventListener('mouseleave', handler, ()=>document, {
2573
+ useEventListener('mouseleave', handler, defaultDocument, {
2522
2574
  passive: true
2523
2575
  });
2524
- useEventListener('mouseenter', handler, ()=>document, {
2576
+ useEventListener('mouseenter', handler, defaultDocument, {
2525
2577
  passive: true
2526
2578
  });
2527
2579
  return isLeft;
@@ -2549,7 +2601,7 @@ const usePermission = (permissionDesc)=>{
2549
2601
  permissionStatus = status;
2550
2602
  on(permissionStatus, 'change', onChange);
2551
2603
  onChange();
2552
- }).catch(noop);
2604
+ }).catch(noop$1);
2553
2605
  return ()=>{
2554
2606
  permissionStatus && off(permissionStatus, 'change', onChange);
2555
2607
  mounted = false;
@@ -2687,7 +2739,7 @@ const useScratch = (target, options = {})=>{
2687
2739
  isScratching: true
2688
2740
  });
2689
2741
  refState.current = newState;
2690
- (optionsRef.current.onScratch || noop)(newState);
2742
+ (optionsRef.current.onScratch || noop$1)(newState);
2691
2743
  return newState;
2692
2744
  });
2693
2745
  });
@@ -2701,7 +2753,7 @@ const useScratch = (target, options = {})=>{
2701
2753
  isScratching: false
2702
2754
  });
2703
2755
  refState.current = endState;
2704
- (optionsRef.current.onScratchEnd || noop)(endState);
2756
+ (optionsRef.current.onScratchEnd || noop$1)(endState);
2705
2757
  setState(endState);
2706
2758
  };
2707
2759
  const startScratching = (docX, docY)=>{
@@ -2734,7 +2786,7 @@ const useScratch = (target, options = {})=>{
2734
2786
  posY: elY
2735
2787
  };
2736
2788
  refState.current = newState;
2737
- (optionsRef.current.onScratchStart || noop)(newState);
2789
+ (optionsRef.current.onScratchStart || noop$1)(newState);
2738
2790
  setState(newState);
2739
2791
  };
2740
2792
  useEventListener('mousedown', (event)=>{
@@ -2761,7 +2813,7 @@ const useScratch = (target, options = {})=>{
2761
2813
  return state;
2762
2814
  };
2763
2815
 
2764
- const useScriptTag = (src, onLoaded = noop, options = defaultOptions$1)=>{
2816
+ const useScriptTag = (src, onLoaded = noop$1, options = defaultOptions$1)=>{
2765
2817
  const { immediate = true, manual = false, type = 'text/javascript', async = true, crossOrigin, referrerPolicy, noModule, defer, attrs = {} } = options;
2766
2818
  const scriptTag = React.useRef(null);
2767
2819
  const _promise = React.useRef(null);
@@ -3171,21 +3223,25 @@ const useSetState = (initialState)=>{
3171
3223
 
3172
3224
  function useSticky(targetElement, { axis = 'y', nav = 0 }, scrollElement) {
3173
3225
  const [isSticky, setSticky] = React.useState(false);
3226
+ const { key: targetKey, ref: targetRef } = useStableTarget(targetElement);
3227
+ const { key: scrollKey, ref: scrollRef } = useStableTarget(scrollElement);
3228
+ const axisRef = useLatest(axis);
3229
+ const navRef = useLatest(nav);
3174
3230
  const { run: scrollHandler } = useThrottleFn(()=>{
3175
- const element = getTargetElement(targetElement);
3231
+ const element = getTargetElement(targetRef.current);
3176
3232
  if (!element) {
3177
3233
  return;
3178
3234
  }
3179
3235
  const rect = element.getBoundingClientRect();
3180
- if (axis === 'y') {
3181
- setSticky((rect == null ? void 0 : rect.top) <= nav);
3236
+ if (axisRef.current === 'y') {
3237
+ setSticky((rect == null ? void 0 : rect.top) <= navRef.current);
3182
3238
  } else {
3183
- setSticky((rect == null ? void 0 : rect.left) <= nav);
3239
+ setSticky((rect == null ? void 0 : rect.left) <= navRef.current);
3184
3240
  }
3185
3241
  }, 50);
3186
3242
  React.useEffect(()=>{
3187
- const element = getTargetElement(targetElement);
3188
- const scrollParent = getTargetElement(scrollElement) || getScrollParent(axis, element);
3243
+ const element = getTargetElement(targetRef.current);
3244
+ const scrollParent = getTargetElement(scrollRef.current) || getScrollParent(axisRef.current, element);
3189
3245
  if (!element || !scrollParent) {
3190
3246
  return;
3191
3247
  }
@@ -3195,9 +3251,8 @@ function useSticky(targetElement, { axis = 'y', nav = 0 }, scrollElement) {
3195
3251
  scrollParent.removeEventListener('scroll', scrollHandler);
3196
3252
  };
3197
3253
  }, [
3198
- axis,
3199
- targetElement,
3200
- scrollElement,
3254
+ targetKey,
3255
+ scrollKey,
3201
3256
  scrollHandler
3202
3257
  ]);
3203
3258
  return [
package/dist/index.d.cts CHANGED
@@ -888,12 +888,13 @@ interface UseEventEmitterReturn<T, U = void> {
888
888
  declare function useEventEmitter<T, U = void>(): readonly [UseEventEmitterEvent<T, U>, (arg1: T, arg2: U) => void, () => void];
889
889
 
890
890
  type Target = BasicTarget<HTMLElement | Element | Window | Document | EventTarget>;
891
- declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
892
- declare function useEventListener<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
893
- declare function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: T, options?: boolean | AddEventListenerOptions): void;
894
- declare function useEventListener<K extends keyof ElementEventMap>(eventName: K, handler: (event: ElementEventMap[K]) => void, element: Element, options?: boolean | AddEventListenerOptions): void;
895
- declare function useEventListener<K = Event>(eventName: string, handler: (event: K) => void, element: EventTarget | null | undefined, options?: boolean | AddEventListenerOptions): void;
896
- declare function useEventListener(eventName: string, handler: (...p: any) => void, element?: Target, options?: boolean | AddEventListenerOptions): void;
891
+ declare function useEventListenerImpl<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
892
+ declare function useEventListenerImpl<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
893
+ declare function useEventListenerImpl<K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: T, options?: boolean | AddEventListenerOptions): void;
894
+ declare function useEventListenerImpl<K extends keyof ElementEventMap>(eventName: K, handler: (event: ElementEventMap[K]) => void, element: Element, options?: boolean | AddEventListenerOptions): void;
895
+ declare function useEventListenerImpl<K = Event>(eventName: string, handler: (event: K) => void, element: EventTarget | null | undefined, options?: boolean | AddEventListenerOptions): void;
896
+ declare function useEventListenerImpl(eventName: string, handler: (...p: any) => void, element?: Target, options?: boolean | AddEventListenerOptions): void;
897
+ declare const useEventListener: typeof useEventListenerImpl;
897
898
 
898
899
  /**
899
900
  * @title useEyeDropper
package/dist/index.d.mts CHANGED
@@ -888,12 +888,13 @@ interface UseEventEmitterReturn<T, U = void> {
888
888
  declare function useEventEmitter<T, U = void>(): readonly [UseEventEmitterEvent<T, U>, (arg1: T, arg2: U) => void, () => void];
889
889
 
890
890
  type Target = BasicTarget<HTMLElement | Element | Window | Document | EventTarget>;
891
- declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
892
- declare function useEventListener<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
893
- declare function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: T, options?: boolean | AddEventListenerOptions): void;
894
- declare function useEventListener<K extends keyof ElementEventMap>(eventName: K, handler: (event: ElementEventMap[K]) => void, element: Element, options?: boolean | AddEventListenerOptions): void;
895
- declare function useEventListener<K = Event>(eventName: string, handler: (event: K) => void, element: EventTarget | null | undefined, options?: boolean | AddEventListenerOptions): void;
896
- declare function useEventListener(eventName: string, handler: (...p: any) => void, element?: Target, options?: boolean | AddEventListenerOptions): void;
891
+ declare function useEventListenerImpl<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
892
+ declare function useEventListenerImpl<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
893
+ declare function useEventListenerImpl<K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: T, options?: boolean | AddEventListenerOptions): void;
894
+ declare function useEventListenerImpl<K extends keyof ElementEventMap>(eventName: K, handler: (event: ElementEventMap[K]) => void, element: Element, options?: boolean | AddEventListenerOptions): void;
895
+ declare function useEventListenerImpl<K = Event>(eventName: string, handler: (event: K) => void, element: EventTarget | null | undefined, options?: boolean | AddEventListenerOptions): void;
896
+ declare function useEventListenerImpl(eventName: string, handler: (...p: any) => void, element?: Target, options?: boolean | AddEventListenerOptions): void;
897
+ declare const useEventListener: typeof useEventListenerImpl;
897
898
 
898
899
  /**
899
900
  * @title useEyeDropper
package/dist/index.d.ts CHANGED
@@ -888,12 +888,13 @@ interface UseEventEmitterReturn<T, U = void> {
888
888
  declare function useEventEmitter<T, U = void>(): readonly [UseEventEmitterEvent<T, U>, (arg1: T, arg2: U) => void, () => void];
889
889
 
890
890
  type Target = BasicTarget<HTMLElement | Element | Window | Document | EventTarget>;
891
- declare function useEventListener<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
892
- declare function useEventListener<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
893
- declare function useEventListener<K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: T, options?: boolean | AddEventListenerOptions): void;
894
- declare function useEventListener<K extends keyof ElementEventMap>(eventName: K, handler: (event: ElementEventMap[K]) => void, element: Element, options?: boolean | AddEventListenerOptions): void;
895
- declare function useEventListener<K = Event>(eventName: string, handler: (event: K) => void, element: EventTarget | null | undefined, options?: boolean | AddEventListenerOptions): void;
896
- declare function useEventListener(eventName: string, handler: (...p: any) => void, element?: Target, options?: boolean | AddEventListenerOptions): void;
891
+ declare function useEventListenerImpl<K extends keyof WindowEventMap>(eventName: K, handler: (event: WindowEventMap[K]) => void, element?: Window, options?: boolean | AddEventListenerOptions): void;
892
+ declare function useEventListenerImpl<K extends keyof DocumentEventMap>(eventName: K, handler: (event: DocumentEventMap[K]) => void, element: Document, options?: boolean | AddEventListenerOptions): void;
893
+ declare function useEventListenerImpl<K extends keyof HTMLElementEventMap, T extends HTMLElement = HTMLDivElement>(eventName: K, handler: (event: HTMLElementEventMap[K]) => void, element: T, options?: boolean | AddEventListenerOptions): void;
894
+ declare function useEventListenerImpl<K extends keyof ElementEventMap>(eventName: K, handler: (event: ElementEventMap[K]) => void, element: Element, options?: boolean | AddEventListenerOptions): void;
895
+ declare function useEventListenerImpl<K = Event>(eventName: string, handler: (event: K) => void, element: EventTarget | null | undefined, options?: boolean | AddEventListenerOptions): void;
896
+ declare function useEventListenerImpl(eventName: string, handler: (...p: any) => void, element?: Target, options?: boolean | AddEventListenerOptions): void;
897
+ declare const useEventListener: typeof useEventListenerImpl;
897
898
 
898
899
  /**
899
900
  * @title useEyeDropper
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@ function isString(val) {
15
15
  const isDev = process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test';
16
16
  const isBrowser = typeof window !== 'undefined';
17
17
  const isNavigator = typeof navigator !== 'undefined';
18
- function noop() {}
18
+ function noop$1() {}
19
19
  const isIOS = isBrowser && ((_window = window) == null ? void 0 : (_window_navigator = _window.navigator) == null ? void 0 : _window_navigator.userAgent) && /iP(?:ad|hone|od)/.test(window.navigator.userAgent);
20
20
 
21
21
  const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;
@@ -105,10 +105,57 @@ const useDeepCompareEffect = (effect, deps)=>{
105
105
  useCustomCompareEffect(effect, deps, isEqual);
106
106
  };
107
107
 
108
- function useEventListener(eventName, handler, element, options = defaultOptions$1) {
108
+ /**
109
+ * Creates a stable identifier for a BasicTarget that can be safely used in effect dependencies.
110
+ *
111
+ * This hook solves the problem where passing unstable function references like `() => document`
112
+ * would cause infinite re-renders when used directly in effect dependency arrays.
113
+ *
114
+ * @param target - The target element (ref, function, or direct element)
115
+ * @param defaultElement - Default element to use if target is undefined
116
+ * @returns A stable reference that only changes when the actual target element changes
117
+ *
118
+ * @example
119
+ * ```tsx
120
+ * // For ref objects: returns the ref itself (stable)
121
+ * const ref = useRef<HTMLDivElement>(null)
122
+ * const key = useStableTarget(ref) // key === ref (stable)
123
+ *
124
+ * // For functions: returns the resolved actual element
125
+ * const key = useStableTarget(() => document) // key === document (stable)
126
+ *
127
+ * // For direct elements: returns the element itself
128
+ * const key = useStableTarget(divElement) // key === divElement (stable)
129
+ * ```
130
+ */ function useStableTarget(target, defaultElement) {
131
+ const targetRef = useRef(target);
132
+ targetRef.current = target;
133
+ // Calculate stable key without memoization
134
+ // For ref objects: return the ref itself (always stable)
135
+ // For functions/direct elements: resolve to the actual element
136
+ let stableKey;
137
+ if (!target) {
138
+ stableKey = defaultElement != null ? defaultElement : null;
139
+ } else if (typeof target === 'object' && 'current' in target) {
140
+ // Ref object - use the ref itself as the stable key
141
+ stableKey = target;
142
+ } else {
143
+ // Function or direct element - resolve to actual element
144
+ stableKey = getTargetElement(target, defaultElement);
145
+ }
146
+ return {
147
+ /** The stable key that can be safely used in effect dependencies */ key: stableKey,
148
+ /** A ref containing the current target (useful for accessing in effects) */ ref: targetRef
149
+ };
150
+ }
151
+
152
+ function useEventListenerImpl(eventName, handler, element, options = defaultOptions$1) {
109
153
  const savedHandler = useLatest(handler);
154
+ const { key: elementKey, ref: elementRef } = useStableTarget(element, defaultWindow);
110
155
  useDeepCompareEffect(()=>{
111
- const targetElement = getTargetElement(element, defaultWindow);
156
+ // Call getTargetElement inside effect to support ref-based targets
157
+ // (ref.current is null during render, only available in commit phase)
158
+ const targetElement = getTargetElement(elementRef.current, defaultWindow);
112
159
  if (!(targetElement && targetElement.addEventListener)) {
113
160
  return;
114
161
  }
@@ -122,10 +169,12 @@ function useEventListener(eventName, handler, element, options = defaultOptions$
122
169
  };
123
170
  }, [
124
171
  eventName,
125
- element,
172
+ elementKey,
126
173
  options
127
174
  ]);
128
175
  }
176
+ function noop() {}
177
+ const useEventListener = isBrowser ? useEventListenerImpl : noop;
129
178
 
130
179
  const useMount = (fn)=>{
131
180
  if (isDev) {
@@ -195,7 +244,7 @@ function _async_to_generator$7(fn) {
195
244
  });
196
245
  };
197
246
  }
198
- const useAsyncEffect = (effect, cleanup = noop, deps)=>{
247
+ const useAsyncEffect = (effect, cleanup = noop$1, deps)=>{
199
248
  const mounted = useMountedState();
200
249
  useEffect(()=>{
201
250
  const execute = ()=>_async_to_generator$7(function*() {
@@ -1089,13 +1138,14 @@ const useDropZone = (target, onDrop)=>{
1089
1138
  const useResizeObserver = (target, callback, options = defaultOptions$1)=>{
1090
1139
  const savedCallback = useLatest(callback);
1091
1140
  const observerRef = useRef();
1141
+ const { key: targetKey, ref: targetRef } = useStableTarget(target);
1092
1142
  const stop = useCallback(()=>{
1093
1143
  if (observerRef.current) {
1094
1144
  observerRef.current.disconnect();
1095
1145
  }
1096
1146
  }, []);
1097
1147
  useDeepCompareEffect(()=>{
1098
- const element = getTargetElement(target);
1148
+ const element = getTargetElement(targetRef.current);
1099
1149
  if (!element) {
1100
1150
  return;
1101
1151
  }
@@ -1103,9 +1153,7 @@ const useResizeObserver = (target, callback, options = defaultOptions$1)=>{
1103
1153
  observerRef.current.observe(element, options);
1104
1154
  return stop;
1105
1155
  }, [
1106
- savedCallback,
1107
- stop,
1108
- target,
1156
+ targetKey,
1109
1157
  options
1110
1158
  ]);
1111
1159
  return stop;
@@ -1216,13 +1264,14 @@ const useElementSize = (target, options = defaultOptions$1)=>{
1216
1264
  const useIntersectionObserver = (target, callback, options = defaultOptions$1)=>{
1217
1265
  const savedCallback = useLatest(callback);
1218
1266
  const observerRef = useRef();
1267
+ const { key: targetKey, ref: targetRef } = useStableTarget(target);
1219
1268
  const stop = useCallback(()=>{
1220
1269
  if (observerRef.current) {
1221
1270
  observerRef.current.disconnect();
1222
1271
  }
1223
1272
  }, []);
1224
1273
  useDeepCompareEffect(()=>{
1225
- const element = getTargetElement(target);
1274
+ const element = getTargetElement(targetRef.current);
1226
1275
  if (!element) {
1227
1276
  return;
1228
1277
  }
@@ -1230,6 +1279,7 @@ const useIntersectionObserver = (target, callback, options = defaultOptions$1)=>
1230
1279
  observerRef.current.observe(element);
1231
1280
  return stop;
1232
1281
  }, [
1282
+ targetKey,
1233
1283
  options
1234
1284
  ]);
1235
1285
  return stop;
@@ -1766,7 +1816,7 @@ const defaultListerOptions = {
1766
1816
  passive: true
1767
1817
  };
1768
1818
  const useScroll = (target, options = defaultOptions$1)=>{
1769
- const { throttle = 0, idle = 200, onStop = noop, onScroll = noop, offset = {
1819
+ const { throttle = 0, idle = 200, onStop = noop$1, onScroll = noop$1, offset = {
1770
1820
  left: 0,
1771
1821
  right: 0,
1772
1822
  top: 0,
@@ -2104,7 +2154,7 @@ const useMediaDevices = (options = {})=>{
2104
2154
  label
2105
2155
  }))
2106
2156
  });
2107
- }).catch(noop);
2157
+ }).catch(noop$1);
2108
2158
  }, []);
2109
2159
  const ensurePermissions = useCallback(()=>_async_to_generator$3(function*() {
2110
2160
  if (!isSupported) {
@@ -2335,13 +2385,14 @@ const useMousePressed = (target, options = defaultOptions$1)=>{
2335
2385
  const useMutationObserver = (callback, target, options = defaultOptions$1)=>{
2336
2386
  const callbackRef = useLatest(callback);
2337
2387
  const observerRef = useRef();
2388
+ const { key: targetKey, ref: targetRef } = useStableTarget(target);
2338
2389
  const stop = useCallback(()=>{
2339
2390
  if (observerRef.current) {
2340
2391
  observerRef.current.disconnect();
2341
2392
  }
2342
2393
  }, []);
2343
2394
  useDeepCompareEffect(()=>{
2344
- const element = getTargetElement(target);
2395
+ const element = getTargetElement(targetRef.current);
2345
2396
  if (!element) {
2346
2397
  return;
2347
2398
  }
@@ -2349,6 +2400,7 @@ const useMutationObserver = (callback, target, options = defaultOptions$1)=>{
2349
2400
  observerRef.current.observe(element, options);
2350
2401
  return stop;
2351
2402
  }, [
2403
+ targetKey,
2352
2404
  options
2353
2405
  ]);
2354
2406
  return stop;
@@ -2507,13 +2559,13 @@ function usePageLeave() {
2507
2559
  const from = event.relatedTarget || event.toElement;
2508
2560
  setIsLeft(!from);
2509
2561
  };
2510
- useEventListener('mouseout', handler, ()=>window, {
2562
+ useEventListener('mouseout', handler, defaultWindow, {
2511
2563
  passive: true
2512
2564
  });
2513
- useEventListener('mouseleave', handler, ()=>document, {
2565
+ useEventListener('mouseleave', handler, defaultDocument, {
2514
2566
  passive: true
2515
2567
  });
2516
- useEventListener('mouseenter', handler, ()=>document, {
2568
+ useEventListener('mouseenter', handler, defaultDocument, {
2517
2569
  passive: true
2518
2570
  });
2519
2571
  return isLeft;
@@ -2541,7 +2593,7 @@ const usePermission = (permissionDesc)=>{
2541
2593
  permissionStatus = status;
2542
2594
  on(permissionStatus, 'change', onChange);
2543
2595
  onChange();
2544
- }).catch(noop);
2596
+ }).catch(noop$1);
2545
2597
  return ()=>{
2546
2598
  permissionStatus && off(permissionStatus, 'change', onChange);
2547
2599
  mounted = false;
@@ -2679,7 +2731,7 @@ const useScratch = (target, options = {})=>{
2679
2731
  isScratching: true
2680
2732
  });
2681
2733
  refState.current = newState;
2682
- (optionsRef.current.onScratch || noop)(newState);
2734
+ (optionsRef.current.onScratch || noop$1)(newState);
2683
2735
  return newState;
2684
2736
  });
2685
2737
  });
@@ -2693,7 +2745,7 @@ const useScratch = (target, options = {})=>{
2693
2745
  isScratching: false
2694
2746
  });
2695
2747
  refState.current = endState;
2696
- (optionsRef.current.onScratchEnd || noop)(endState);
2748
+ (optionsRef.current.onScratchEnd || noop$1)(endState);
2697
2749
  setState(endState);
2698
2750
  };
2699
2751
  const startScratching = (docX, docY)=>{
@@ -2726,7 +2778,7 @@ const useScratch = (target, options = {})=>{
2726
2778
  posY: elY
2727
2779
  };
2728
2780
  refState.current = newState;
2729
- (optionsRef.current.onScratchStart || noop)(newState);
2781
+ (optionsRef.current.onScratchStart || noop$1)(newState);
2730
2782
  setState(newState);
2731
2783
  };
2732
2784
  useEventListener('mousedown', (event)=>{
@@ -2753,7 +2805,7 @@ const useScratch = (target, options = {})=>{
2753
2805
  return state;
2754
2806
  };
2755
2807
 
2756
- const useScriptTag = (src, onLoaded = noop, options = defaultOptions$1)=>{
2808
+ const useScriptTag = (src, onLoaded = noop$1, options = defaultOptions$1)=>{
2757
2809
  const { immediate = true, manual = false, type = 'text/javascript', async = true, crossOrigin, referrerPolicy, noModule, defer, attrs = {} } = options;
2758
2810
  const scriptTag = useRef(null);
2759
2811
  const _promise = useRef(null);
@@ -3163,21 +3215,25 @@ const useSetState = (initialState)=>{
3163
3215
 
3164
3216
  function useSticky(targetElement, { axis = 'y', nav = 0 }, scrollElement) {
3165
3217
  const [isSticky, setSticky] = useState(false);
3218
+ const { key: targetKey, ref: targetRef } = useStableTarget(targetElement);
3219
+ const { key: scrollKey, ref: scrollRef } = useStableTarget(scrollElement);
3220
+ const axisRef = useLatest(axis);
3221
+ const navRef = useLatest(nav);
3166
3222
  const { run: scrollHandler } = useThrottleFn(()=>{
3167
- const element = getTargetElement(targetElement);
3223
+ const element = getTargetElement(targetRef.current);
3168
3224
  if (!element) {
3169
3225
  return;
3170
3226
  }
3171
3227
  const rect = element.getBoundingClientRect();
3172
- if (axis === 'y') {
3173
- setSticky((rect == null ? void 0 : rect.top) <= nav);
3228
+ if (axisRef.current === 'y') {
3229
+ setSticky((rect == null ? void 0 : rect.top) <= navRef.current);
3174
3230
  } else {
3175
- setSticky((rect == null ? void 0 : rect.left) <= nav);
3231
+ setSticky((rect == null ? void 0 : rect.left) <= navRef.current);
3176
3232
  }
3177
3233
  }, 50);
3178
3234
  useEffect(()=>{
3179
- const element = getTargetElement(targetElement);
3180
- const scrollParent = getTargetElement(scrollElement) || getScrollParent(axis, element);
3235
+ const element = getTargetElement(targetRef.current);
3236
+ const scrollParent = getTargetElement(scrollRef.current) || getScrollParent(axisRef.current, element);
3181
3237
  if (!element || !scrollParent) {
3182
3238
  return;
3183
3239
  }
@@ -3187,9 +3243,8 @@ function useSticky(targetElement, { axis = 'y', nav = 0 }, scrollElement) {
3187
3243
  scrollParent.removeEventListener('scroll', scrollHandler);
3188
3244
  };
3189
3245
  }, [
3190
- axis,
3191
- targetElement,
3192
- scrollElement,
3246
+ targetKey,
3247
+ scrollKey,
3193
3248
  scrollHandler
3194
3249
  ]);
3195
3250
  return [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactuses/core",
3
- "version": "6.1.9",
3
+ "version": "6.1.11",
4
4
  "license": "Unlicense",
5
5
  "homepage": "https://www.reactuse.com/",
6
6
  "repository": {