@reactuses/core 2.0.2 → 2.2.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/dist/index.cjs CHANGED
@@ -116,38 +116,41 @@ const StorageSerializers = {
116
116
  }
117
117
  };
118
118
  function useStorage(key, defaults, getStorage, options = {}) {
119
- var _a;
119
+ const defaultOnError = React.useCallback((e) => {
120
+ console.error(e);
121
+ }, []);
120
122
  let storage;
121
- const {
122
- onError = (e) => {
123
- console.error(e);
124
- }
125
- } = options;
126
- const data = defaults;
123
+ const { onError = defaultOnError, ignoreDefaults = true } = options;
127
124
  try {
128
125
  storage = getStorage();
129
126
  } catch (err) {
130
- console.error(err);
127
+ onError(err);
131
128
  }
132
129
  const type = guessSerializerType(defaults);
133
- const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type];
134
- const getStoredValue = useEvent(() => {
135
- try {
136
- const raw = storage == null ? void 0 : storage.getItem(key);
137
- if (raw !== void 0 && raw !== null) {
138
- return serializer.read(raw);
139
- } else {
140
- storage == null ? void 0 : storage.setItem(key, serializer.write(data));
141
- return data;
142
- }
143
- } catch (e) {
144
- onError(e);
145
- }
146
- });
130
+ const serializer = React.useMemo(
131
+ () => {
132
+ var _a;
133
+ return (_a = options.serializer) != null ? _a : StorageSerializers[type];
134
+ },
135
+ [options.serializer, type]
136
+ );
147
137
  const [state, setState] = React.useState(defaults);
148
138
  React.useEffect(() => {
139
+ const getStoredValue = () => {
140
+ try {
141
+ const raw = storage == null ? void 0 : storage.getItem(key);
142
+ if (raw !== void 0 && raw !== null && ignoreDefaults) {
143
+ return serializer.read(raw);
144
+ } else {
145
+ storage == null ? void 0 : storage.setItem(key, serializer.write(defaults));
146
+ return defaults;
147
+ }
148
+ } catch (e) {
149
+ onError(e);
150
+ }
151
+ };
149
152
  setState(getStoredValue());
150
- }, [getStoredValue, key]);
153
+ }, [key, ignoreDefaults, defaults, serializer, storage, onError]);
151
154
  const updateState = useEvent(
152
155
  (valOrFunc) => {
153
156
  const currentState = isFunction(valOrFunc) ? valOrFunc(state) : valOrFunc;
@@ -261,7 +264,9 @@ function useDarkMode(options = {}) {
261
264
  } = options;
262
265
  const prefersDarkMode = usePreferredDark(false);
263
266
  const value = initialValue ? initialValue : prefersDarkMode ? "dark" : "light";
264
- const [dark, setDark] = useStorage(storageKey, value, storage);
267
+ const [dark, setDark] = useStorage(storageKey, value, storage, {
268
+ ignoreDefaults: false
269
+ });
265
270
  const wrappedSetDark = React.useCallback(
266
271
  (latestDark) => {
267
272
  const element = window == null ? void 0 : window.document.querySelector(selector);
@@ -501,6 +506,13 @@ function getTargetElement(target, defaultElement) {
501
506
  }
502
507
  return targetElement;
503
508
  }
509
+ function useLatestElement(target, defaultElement) {
510
+ const ref = React.useRef(getTargetElement(target, defaultElement));
511
+ React.useEffect(() => {
512
+ ref.current = getTargetElement(target, defaultElement);
513
+ }, [defaultElement, target]);
514
+ return ref;
515
+ }
504
516
 
505
517
  const isPrimitive$1 = (val) => val !== Object(val);
506
518
  function useCustomCompareEffect(effect, deps, depsEqual) {
@@ -547,17 +559,21 @@ function useDeepCompareEffect(effect, deps) {
547
559
 
548
560
  function useEventListener(eventName, handler, element, options) {
549
561
  const savedHandler = useLatest(handler);
550
- const targetElement = getTargetElement(element, defaultWindow);
562
+ const targetElementRef = useLatestElement(element, defaultWindow);
551
563
  useDeepCompareEffect(() => {
564
+ const targetElement = targetElementRef.current;
552
565
  if (!(targetElement && targetElement.addEventListener)) {
553
566
  return;
554
567
  }
555
568
  const eventListener = (event) => savedHandler.current(event);
556
569
  on(targetElement, eventName, eventListener, options);
557
570
  return () => {
571
+ if (!(targetElement && targetElement.removeEventListener)) {
572
+ return;
573
+ }
558
574
  off(targetElement, eventName, eventListener);
559
575
  };
560
- }, [eventName, targetElement, options, savedHandler]);
576
+ }, [eventName, targetElementRef.current, options, savedHandler]);
561
577
  }
562
578
 
563
579
  function useCounter(initialValue = 0, max = null, min = null) {
@@ -701,20 +717,20 @@ function useFavicon(href, baseUrl = "", rel = "icon") {
701
717
  function useMutationObserver(callback, target, options = {}) {
702
718
  const callbackRef = useLatest(callback);
703
719
  const observerRef = React.useRef();
704
- const element = getTargetElement(target);
720
+ const element = useLatestElement(target);
705
721
  const stop = React.useCallback(() => {
706
722
  if (observerRef.current) {
707
723
  observerRef.current.disconnect();
708
724
  }
709
725
  }, []);
710
726
  useDeepCompareEffect(() => {
711
- if (!element) {
727
+ if (!element.current) {
712
728
  return;
713
729
  }
714
730
  observerRef.current = new MutationObserver(callbackRef.current);
715
- observerRef.current.observe(element, options);
731
+ observerRef.current.observe(element.current, options);
716
732
  return stop;
717
- }, [options, element]);
733
+ }, [options, element.current]);
718
734
  return stop;
719
735
  }
720
736
 
@@ -997,7 +1013,7 @@ function useMediaDevices() {
997
1013
  }, []);
998
1014
  return state;
999
1015
  }
1000
- const useMediaDevicesMock = () => ({});
1016
+ const useMediaDevicesMock = () => ({ devices: [] });
1001
1017
  var useMediaDevices$1 = isNavigator && !!navigator.mediaDevices ? useMediaDevices : useMediaDevicesMock;
1002
1018
 
1003
1019
  function useTextDirection(options = {}) {
@@ -1499,23 +1515,23 @@ function useOrientation(initialState = defaultState) {
1499
1515
  function useIntersectionObserver(target, callback, options = {}) {
1500
1516
  const savedCallback = useLatest(callback);
1501
1517
  const observerRef = React.useRef();
1502
- const element = getTargetElement(target);
1518
+ const element = useLatestElement(target);
1503
1519
  const stop = React.useCallback(() => {
1504
1520
  if (observerRef.current) {
1505
1521
  observerRef.current.disconnect();
1506
1522
  }
1507
1523
  }, []);
1508
1524
  useDeepCompareEffect(() => {
1509
- if (!element) {
1525
+ if (!element.current) {
1510
1526
  return;
1511
1527
  }
1512
1528
  observerRef.current = new IntersectionObserver(
1513
1529
  savedCallback.current,
1514
1530
  options
1515
1531
  );
1516
- observerRef.current.observe(element);
1532
+ observerRef.current.observe(element.current);
1517
1533
  return stop;
1518
- }, [options, element]);
1534
+ }, [options, element.current]);
1519
1535
  return stop;
1520
1536
  }
1521
1537
 
@@ -1555,27 +1571,26 @@ function useDocumentVisibility() {
1555
1571
  function useResizeObserver(target, callback, options = {}) {
1556
1572
  const savedCallback = useLatest(callback);
1557
1573
  const observerRef = React.useRef();
1558
- const element = getTargetElement(target);
1574
+ const element = useLatestElement(target);
1559
1575
  const stop = React.useCallback(() => {
1560
1576
  if (observerRef.current) {
1561
1577
  observerRef.current.disconnect();
1562
1578
  }
1563
1579
  }, []);
1564
1580
  useDeepCompareEffect(() => {
1565
- if (!element) {
1581
+ if (!element.current) {
1566
1582
  return;
1567
1583
  }
1568
1584
  observerRef.current = new ResizeObserver(savedCallback.current);
1569
- observerRef.current.observe(element, options);
1585
+ observerRef.current.observe(element.current, options);
1570
1586
  return stop;
1571
- }, [options, element]);
1587
+ }, [options, element.current]);
1572
1588
  return stop;
1573
1589
  }
1574
1590
 
1575
1591
  function useDropZone(target, onDrop) {
1576
1592
  const [over, setOver] = React.useState(false);
1577
1593
  const counter = React.useRef(0);
1578
- const element = getTargetElement(target);
1579
1594
  useEventListener(
1580
1595
  "dragenter",
1581
1596
  (event) => {
@@ -1583,14 +1598,14 @@ function useDropZone(target, onDrop) {
1583
1598
  counter.current += 1;
1584
1599
  setOver(true);
1585
1600
  },
1586
- element
1601
+ target
1587
1602
  );
1588
1603
  useEventListener(
1589
1604
  "dragover",
1590
1605
  (event) => {
1591
1606
  event.preventDefault();
1592
1607
  },
1593
- element
1608
+ target
1594
1609
  );
1595
1610
  useEventListener(
1596
1611
  "dragleave",
@@ -1601,7 +1616,7 @@ function useDropZone(target, onDrop) {
1601
1616
  setOver(false);
1602
1617
  }
1603
1618
  },
1604
- element
1619
+ target
1605
1620
  );
1606
1621
  useEventListener(
1607
1622
  "drop",
@@ -1613,7 +1628,7 @@ function useDropZone(target, onDrop) {
1613
1628
  const files = Array.from((_b = (_a = event.dataTransfer) == null ? void 0 : _a.files) != null ? _b : []);
1614
1629
  onDrop == null ? void 0 : onDrop(files.length === 0 ? null : files);
1615
1630
  },
1616
- element
1631
+ target
1617
1632
  );
1618
1633
  return over;
1619
1634
  }
@@ -1793,25 +1808,27 @@ function useInfiniteScroll(target, onLoadMore, options = {}) {
1793
1808
  [direction]: (_b = options.distance) != null ? _b : 0
1794
1809
  }, options.offset)
1795
1810
  }));
1796
- const element = getTargetElement(target);
1811
+ const element = useLatestElement(target);
1812
+ const latestOptions = useLatest(options);
1797
1813
  const di = state[3][direction];
1798
1814
  useUpdateEffect(() => {
1815
+ const opts = latestOptions.current;
1799
1816
  const fn = () => __async$2(this, null, function* () {
1800
- var _a2, _b2;
1817
+ var _a2, _b2, _c, _d;
1801
1818
  const previous = {
1802
- height: (_a2 = element == null ? void 0 : element.scrollHeight) != null ? _a2 : 0,
1803
- width: (_b2 = element == null ? void 0 : element.scrollWidth) != null ? _b2 : 0
1819
+ height: (_b2 = (_a2 = element.current) == null ? void 0 : _a2.scrollHeight) != null ? _b2 : 0,
1820
+ width: (_d = (_c = element.current) == null ? void 0 : _c.scrollWidth) != null ? _d : 0
1804
1821
  };
1805
1822
  yield savedLoadMore.current(state);
1806
- if (options.preserveScrollPosition && element) {
1807
- element.scrollTo({
1808
- top: element.scrollHeight - previous.height,
1809
- left: element.scrollWidth - previous.width
1823
+ if (opts.preserveScrollPosition && element.current) {
1824
+ element.current.scrollTo({
1825
+ top: element.current.scrollHeight - previous.height,
1826
+ left: element.current.scrollWidth - previous.width
1810
1827
  });
1811
1828
  }
1812
1829
  });
1813
1830
  fn();
1814
- }, [di, options.preserveScrollPosition, element]);
1831
+ }, [di]);
1815
1832
  }
1816
1833
 
1817
1834
  const defaultEvents = [
@@ -1848,7 +1865,7 @@ function useMousePressed(target, options = {}) {
1848
1865
  const { touch = true, drag = true, initialValue = false } = options;
1849
1866
  const [pressed, setPressed] = React.useState(initialValue);
1850
1867
  const [sourceType, setSourceType] = React.useState(null);
1851
- const element = getTargetElement(target);
1868
+ const elementRef = useLatestElement(target);
1852
1869
  const onPressed = React.useCallback(
1853
1870
  (srcType) => () => {
1854
1871
  setPressed(true);
@@ -1864,6 +1881,7 @@ function useMousePressed(target, options = {}) {
1864
1881
  useEventListener("mouseleave", onReleased, () => window, { passive: true });
1865
1882
  useEventListener("mouseup", onReleased, () => window, { passive: true });
1866
1883
  React.useEffect(() => {
1884
+ const element = elementRef.current;
1867
1885
  if (drag) {
1868
1886
  element == null ? void 0 : element.addEventListener("dragstart", onPressed("mouse"), {
1869
1887
  passive: true
@@ -1898,7 +1916,7 @@ function useMousePressed(target, options = {}) {
1898
1916
  element == null ? void 0 : element.removeEventListener("touchcancel", onReleased);
1899
1917
  }
1900
1918
  };
1901
- }, [drag, onPressed, onReleased, element, touch]);
1919
+ }, [drag, onPressed, onReleased, touch, elementRef.current]);
1902
1920
  return [pressed, sourceType];
1903
1921
  }
1904
1922
 
@@ -1915,32 +1933,34 @@ function preventDefault(rawEvent) {
1915
1933
  function useScrollLock(target, initialState = false) {
1916
1934
  const [locked, setLocked] = React.useState(initialState);
1917
1935
  const initialOverflowRef = React.useRef("scroll");
1918
- const element = getTargetElement(target);
1936
+ const element = useLatestElement(target);
1919
1937
  React.useEffect(() => {
1920
- if (element) {
1921
- initialOverflowRef.current = element.style.overflow;
1938
+ if (element.current) {
1939
+ initialOverflowRef.current = element.current.style.overflow;
1922
1940
  if (locked) {
1923
- element.style.overflow = "hidden";
1941
+ element.current.style.overflow = "hidden";
1924
1942
  }
1925
1943
  }
1926
- }, [locked, element]);
1944
+ }, [locked, element.current]);
1927
1945
  const lock = useEvent(() => {
1928
- if (!element || locked) {
1946
+ if (!element.current || locked) {
1929
1947
  return;
1930
1948
  }
1931
1949
  if (isIOS) {
1932
- element.addEventListener("touchmove", preventDefault, { passive: false });
1950
+ element.current.addEventListener("touchmove", preventDefault, {
1951
+ passive: false
1952
+ });
1933
1953
  }
1934
1954
  setLocked(true);
1935
1955
  });
1936
1956
  const unlock = useEvent(() => {
1937
- if (!element || !locked) {
1957
+ if (!element.current || !locked) {
1938
1958
  return;
1939
1959
  }
1940
1960
  if (isIOS) {
1941
- element.removeEventListener("touchmove", preventDefault);
1961
+ element.current.removeEventListener("touchmove", preventDefault);
1942
1962
  }
1943
- element.style.overflow = initialOverflowRef.current;
1963
+ element.current.style.overflow = initialOverflowRef.current;
1944
1964
  setLocked(false);
1945
1965
  });
1946
1966
  const set = useEvent((flag) => {
@@ -2124,14 +2144,15 @@ function useActiveElement() {
2124
2144
 
2125
2145
  function useDraggable(target, options = {}) {
2126
2146
  var _a, _b;
2127
- const draggingElement = getTargetElement(
2128
- options.draggingElement,
2129
- defaultWindow
2130
- );
2131
- const draggingHandle = getTargetElement((_a = options.handle) != null ? _a : target);
2147
+ const draggingElement = options.draggingElement;
2148
+ const draggingHandle = (_a = options.handle) != null ? _a : target;
2132
2149
  const [position, setPositon] = React.useState(
2133
2150
  (_b = options.initialValue) != null ? _b : { x: 0, y: 0 }
2134
2151
  );
2152
+ React.useEffect(() => {
2153
+ var _a2;
2154
+ setPositon((_a2 = options.initialValue) != null ? _a2 : { x: 0, y: 0 });
2155
+ }, [options.initialValue]);
2135
2156
  const [pressedDelta, setPressedDelta] = React.useState();
2136
2157
  const filterEvent = (e) => {
2137
2158
  if (options.pointerTypes) {
@@ -2623,11 +2644,11 @@ function useScrollIntoView({
2623
2644
  cancelAnimationFrame(frameID.current);
2624
2645
  }
2625
2646
  };
2647
+ const element = useLatestElement(targetElement);
2626
2648
  const scrollIntoView = useEvent(
2627
2649
  ({ alignment = "start" } = {}) => {
2628
2650
  var _a;
2629
- const element = getTargetElement(targetElement);
2630
- const parent = getTargetElement(scrollElement) || getScrollParent(axis, element);
2651
+ const parent = getTargetElement(scrollElement) || getScrollParent(axis, element.current);
2631
2652
  shouldStop.current = false;
2632
2653
  if (frameID.current) {
2633
2654
  cancel();
@@ -2635,7 +2656,7 @@ function useScrollIntoView({
2635
2656
  const start = (_a = getScrollStart({ parent, axis })) != null ? _a : 0;
2636
2657
  const change = getRelativePosition({
2637
2658
  parent,
2638
- target: element,
2659
+ target: element.current,
2639
2660
  axis,
2640
2661
  alignment,
2641
2662
  offset,
@@ -2687,13 +2708,12 @@ const useSticky = ({
2687
2708
  nav = 0
2688
2709
  }) => {
2689
2710
  const [isSticky, setSticky] = React.useState(false);
2690
- const element = getTargetElement(targetElement);
2691
- const scrollParent = getTargetElement(scrollElement) || getScrollParent(axis, element);
2711
+ const element = useLatestElement(targetElement);
2692
2712
  const { run: scrollHandler } = useThrottleFn(() => {
2693
- if (!element) {
2713
+ if (!element.current) {
2694
2714
  return;
2695
2715
  }
2696
- const rect = element.getBoundingClientRect();
2716
+ const rect = element.current.getBoundingClientRect();
2697
2717
  if (axis === "y") {
2698
2718
  setSticky((rect == null ? void 0 : rect.top) <= nav);
2699
2719
  } else {
@@ -2701,7 +2721,8 @@ const useSticky = ({
2701
2721
  }
2702
2722
  }, 50);
2703
2723
  React.useEffect(() => {
2704
- if (!element || !scrollParent) {
2724
+ const scrollParent = getTargetElement(scrollElement) || getScrollParent(axis, element.current);
2725
+ if (!element.current || !scrollParent) {
2705
2726
  return;
2706
2727
  }
2707
2728
  scrollParent.addEventListener("scroll", scrollHandler);
@@ -2709,7 +2730,7 @@ const useSticky = ({
2709
2730
  return () => {
2710
2731
  scrollParent.removeEventListener("scroll", scrollHandler);
2711
2732
  };
2712
- }, [axis, element, scrollHandler, scrollParent]);
2733
+ }, [axis, element, scrollElement, scrollHandler]);
2713
2734
  return [isSticky, setSticky];
2714
2735
  };
2715
2736
 
package/dist/index.d.ts CHANGED
@@ -27,6 +27,10 @@ interface UseStorageOptions<T> {
27
27
  * Default log error to `console.error`
28
28
  */
29
29
  onError?: (error: unknown) => void;
30
+ /**
31
+ * ignore default value when storage has value
32
+ */
33
+ ignoreDefaults?: boolean;
30
34
  }
31
35
 
32
36
  declare function useLocalStorage(key: string, defaults: string, options?: UseStorageOptions<string>): readonly [string | null, Dispatch<SetStateAction<string | null>>];
@@ -74,8 +78,7 @@ interface UseDarkOptions<T> {
74
78
  */
75
79
  attribute?: string;
76
80
  /**
77
- * The initial value write the target element, defaultValue follow system prefer color
78
- * must be set in SSR
81
+ * The initial classname value write the target element, Otherwise, it will follow the system by default
79
82
  * @default 'light | dark'
80
83
  */
81
84
  initialValue?: T;
@@ -92,7 +95,7 @@ interface UseDarkOptions<T> {
92
95
  */
93
96
  storage?: () => Storage;
94
97
  }
95
- declare function useDarkMode<T extends string | "light" | "dark">(options?: UseDarkOptions<T>): readonly [T | null, (latestDark: T) => void];
98
+ declare function useDarkMode<T extends string>(options?: UseDarkOptions<T>): readonly [T | null, (latestDark: T) => void];
96
99
 
97
100
  declare function useMediaQuery(query: string, defaultState?: boolean): boolean;
98
101
 
@@ -275,7 +278,15 @@ declare function useObjectUrl(object: Blob | MediaSource | undefined): string |
275
278
 
276
279
  declare function useIdle(ms?: number, initialState?: boolean, events?: (keyof WindowEventMap)[]): boolean;
277
280
 
278
- declare const _default$3: () => {};
281
+ declare function useMediaDevices(): {
282
+ devices: {
283
+ deviceId: string;
284
+ groupId: string;
285
+ kind: MediaDeviceKind;
286
+ label: string;
287
+ }[];
288
+ };
289
+ declare const _default$3: typeof useMediaDevices;
279
290
 
280
291
  type UseTextDirectionValue = "ltr" | "rtl" | "auto";
281
292
  interface UseTextDirectionOptions {
@@ -416,7 +427,7 @@ declare function useDocumentVisibility(): DocumentVisibilityState;
416
427
 
417
428
  declare function useResizeObserver(target: BasicTarget, callback: ResizeObserverCallback, options?: ResizeObserverOptions): () => void;
418
429
 
419
- declare function useDropZone(target: BasicTarget<HTMLElement>, onDrop?: (files: File[] | null) => void): boolean;
430
+ declare function useDropZone(target: BasicTarget<EventTarget>, onDrop?: (files: File[] | null) => void): boolean;
420
431
 
421
432
  interface UseFileDialogOptions {
422
433
  /**
@@ -518,7 +529,7 @@ interface UseInfiniteScrollOptions extends UseScrollOptions {
518
529
  */
519
530
  preserveScrollPosition?: boolean;
520
531
  }
521
- declare function useInfiniteScroll(target: BasicTarget<HTMLElement | SVGElement | Window | Document>, onLoadMore: (state: ReturnType<typeof useScroll>) => void | Promise<void>, options?: UseInfiniteScrollOptions): void;
532
+ declare function useInfiniteScroll(target: BasicTarget<HTMLElement | SVGElement>, onLoadMore: (state: ReturnType<typeof useScroll>) => void | Promise<void>, options?: UseInfiniteScrollOptions): void;
522
533
 
523
534
  type KeyModifier = "Alt" | "AltGraph" | "CapsLock" | "Control" | "Fn" | "FnLock" | "Meta" | "NumLock" | "ScrollLock" | "Shift" | "Symbol" | "SymbolLock";
524
535
  interface UseModifierOptions {
@@ -563,7 +574,7 @@ interface MousePressedOptions {
563
574
  type MouseSourceType = "mouse" | "touch" | null;
564
575
  declare function useMousePressed(target?: BasicTarget, options?: MousePressedOptions): readonly [boolean, MouseSourceType];
565
576
 
566
- declare function useScrollLock(target: BasicTarget<HTMLElement | SVGElement | Window | Document>, initialState?: boolean): readonly [boolean, (flag: boolean) => void];
577
+ declare function useScrollLock(target: BasicTarget<HTMLElement>, initialState?: boolean): readonly [boolean, (flag: boolean) => void];
567
578
 
568
579
  declare function useElementSize(target: BasicTarget, options?: ResizeObserverOptions): readonly [number, number];
569
580
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import React, { useRef, useEffect, useLayoutEffect, useCallback, useState, useReducer, useMemo } from 'react';
1
+ import React, { useRef, useEffect, useLayoutEffect, useCallback, useMemo, useState, useReducer } from 'react';
2
2
  import { throttle, debounce, isEqual } from 'lodash';
3
3
 
4
4
  function usePrevious(state) {
@@ -108,38 +108,41 @@ const StorageSerializers = {
108
108
  }
109
109
  };
110
110
  function useStorage(key, defaults, getStorage, options = {}) {
111
- var _a;
111
+ const defaultOnError = useCallback((e) => {
112
+ console.error(e);
113
+ }, []);
112
114
  let storage;
113
- const {
114
- onError = (e) => {
115
- console.error(e);
116
- }
117
- } = options;
118
- const data = defaults;
115
+ const { onError = defaultOnError, ignoreDefaults = true } = options;
119
116
  try {
120
117
  storage = getStorage();
121
118
  } catch (err) {
122
- console.error(err);
119
+ onError(err);
123
120
  }
124
121
  const type = guessSerializerType(defaults);
125
- const serializer = (_a = options.serializer) != null ? _a : StorageSerializers[type];
126
- const getStoredValue = useEvent(() => {
127
- try {
128
- const raw = storage == null ? void 0 : storage.getItem(key);
129
- if (raw !== void 0 && raw !== null) {
130
- return serializer.read(raw);
131
- } else {
132
- storage == null ? void 0 : storage.setItem(key, serializer.write(data));
133
- return data;
134
- }
135
- } catch (e) {
136
- onError(e);
137
- }
138
- });
122
+ const serializer = useMemo(
123
+ () => {
124
+ var _a;
125
+ return (_a = options.serializer) != null ? _a : StorageSerializers[type];
126
+ },
127
+ [options.serializer, type]
128
+ );
139
129
  const [state, setState] = useState(defaults);
140
130
  useEffect(() => {
131
+ const getStoredValue = () => {
132
+ try {
133
+ const raw = storage == null ? void 0 : storage.getItem(key);
134
+ if (raw !== void 0 && raw !== null && ignoreDefaults) {
135
+ return serializer.read(raw);
136
+ } else {
137
+ storage == null ? void 0 : storage.setItem(key, serializer.write(defaults));
138
+ return defaults;
139
+ }
140
+ } catch (e) {
141
+ onError(e);
142
+ }
143
+ };
141
144
  setState(getStoredValue());
142
- }, [getStoredValue, key]);
145
+ }, [key, ignoreDefaults, defaults, serializer, storage, onError]);
143
146
  const updateState = useEvent(
144
147
  (valOrFunc) => {
145
148
  const currentState = isFunction(valOrFunc) ? valOrFunc(state) : valOrFunc;
@@ -253,7 +256,9 @@ function useDarkMode(options = {}) {
253
256
  } = options;
254
257
  const prefersDarkMode = usePreferredDark(false);
255
258
  const value = initialValue ? initialValue : prefersDarkMode ? "dark" : "light";
256
- const [dark, setDark] = useStorage(storageKey, value, storage);
259
+ const [dark, setDark] = useStorage(storageKey, value, storage, {
260
+ ignoreDefaults: false
261
+ });
257
262
  const wrappedSetDark = useCallback(
258
263
  (latestDark) => {
259
264
  const element = window == null ? void 0 : window.document.querySelector(selector);
@@ -493,6 +498,13 @@ function getTargetElement(target, defaultElement) {
493
498
  }
494
499
  return targetElement;
495
500
  }
501
+ function useLatestElement(target, defaultElement) {
502
+ const ref = useRef(getTargetElement(target, defaultElement));
503
+ useEffect(() => {
504
+ ref.current = getTargetElement(target, defaultElement);
505
+ }, [defaultElement, target]);
506
+ return ref;
507
+ }
496
508
 
497
509
  const isPrimitive$1 = (val) => val !== Object(val);
498
510
  function useCustomCompareEffect(effect, deps, depsEqual) {
@@ -539,17 +551,21 @@ function useDeepCompareEffect(effect, deps) {
539
551
 
540
552
  function useEventListener(eventName, handler, element, options) {
541
553
  const savedHandler = useLatest(handler);
542
- const targetElement = getTargetElement(element, defaultWindow);
554
+ const targetElementRef = useLatestElement(element, defaultWindow);
543
555
  useDeepCompareEffect(() => {
556
+ const targetElement = targetElementRef.current;
544
557
  if (!(targetElement && targetElement.addEventListener)) {
545
558
  return;
546
559
  }
547
560
  const eventListener = (event) => savedHandler.current(event);
548
561
  on(targetElement, eventName, eventListener, options);
549
562
  return () => {
563
+ if (!(targetElement && targetElement.removeEventListener)) {
564
+ return;
565
+ }
550
566
  off(targetElement, eventName, eventListener);
551
567
  };
552
- }, [eventName, targetElement, options, savedHandler]);
568
+ }, [eventName, targetElementRef.current, options, savedHandler]);
553
569
  }
554
570
 
555
571
  function useCounter(initialValue = 0, max = null, min = null) {
@@ -693,20 +709,20 @@ function useFavicon(href, baseUrl = "", rel = "icon") {
693
709
  function useMutationObserver(callback, target, options = {}) {
694
710
  const callbackRef = useLatest(callback);
695
711
  const observerRef = useRef();
696
- const element = getTargetElement(target);
712
+ const element = useLatestElement(target);
697
713
  const stop = useCallback(() => {
698
714
  if (observerRef.current) {
699
715
  observerRef.current.disconnect();
700
716
  }
701
717
  }, []);
702
718
  useDeepCompareEffect(() => {
703
- if (!element) {
719
+ if (!element.current) {
704
720
  return;
705
721
  }
706
722
  observerRef.current = new MutationObserver(callbackRef.current);
707
- observerRef.current.observe(element, options);
723
+ observerRef.current.observe(element.current, options);
708
724
  return stop;
709
- }, [options, element]);
725
+ }, [options, element.current]);
710
726
  return stop;
711
727
  }
712
728
 
@@ -989,7 +1005,7 @@ function useMediaDevices() {
989
1005
  }, []);
990
1006
  return state;
991
1007
  }
992
- const useMediaDevicesMock = () => ({});
1008
+ const useMediaDevicesMock = () => ({ devices: [] });
993
1009
  var useMediaDevices$1 = isNavigator && !!navigator.mediaDevices ? useMediaDevices : useMediaDevicesMock;
994
1010
 
995
1011
  function useTextDirection(options = {}) {
@@ -1491,23 +1507,23 @@ function useOrientation(initialState = defaultState) {
1491
1507
  function useIntersectionObserver(target, callback, options = {}) {
1492
1508
  const savedCallback = useLatest(callback);
1493
1509
  const observerRef = useRef();
1494
- const element = getTargetElement(target);
1510
+ const element = useLatestElement(target);
1495
1511
  const stop = useCallback(() => {
1496
1512
  if (observerRef.current) {
1497
1513
  observerRef.current.disconnect();
1498
1514
  }
1499
1515
  }, []);
1500
1516
  useDeepCompareEffect(() => {
1501
- if (!element) {
1517
+ if (!element.current) {
1502
1518
  return;
1503
1519
  }
1504
1520
  observerRef.current = new IntersectionObserver(
1505
1521
  savedCallback.current,
1506
1522
  options
1507
1523
  );
1508
- observerRef.current.observe(element);
1524
+ observerRef.current.observe(element.current);
1509
1525
  return stop;
1510
- }, [options, element]);
1526
+ }, [options, element.current]);
1511
1527
  return stop;
1512
1528
  }
1513
1529
 
@@ -1547,27 +1563,26 @@ function useDocumentVisibility() {
1547
1563
  function useResizeObserver(target, callback, options = {}) {
1548
1564
  const savedCallback = useLatest(callback);
1549
1565
  const observerRef = useRef();
1550
- const element = getTargetElement(target);
1566
+ const element = useLatestElement(target);
1551
1567
  const stop = useCallback(() => {
1552
1568
  if (observerRef.current) {
1553
1569
  observerRef.current.disconnect();
1554
1570
  }
1555
1571
  }, []);
1556
1572
  useDeepCompareEffect(() => {
1557
- if (!element) {
1573
+ if (!element.current) {
1558
1574
  return;
1559
1575
  }
1560
1576
  observerRef.current = new ResizeObserver(savedCallback.current);
1561
- observerRef.current.observe(element, options);
1577
+ observerRef.current.observe(element.current, options);
1562
1578
  return stop;
1563
- }, [options, element]);
1579
+ }, [options, element.current]);
1564
1580
  return stop;
1565
1581
  }
1566
1582
 
1567
1583
  function useDropZone(target, onDrop) {
1568
1584
  const [over, setOver] = useState(false);
1569
1585
  const counter = useRef(0);
1570
- const element = getTargetElement(target);
1571
1586
  useEventListener(
1572
1587
  "dragenter",
1573
1588
  (event) => {
@@ -1575,14 +1590,14 @@ function useDropZone(target, onDrop) {
1575
1590
  counter.current += 1;
1576
1591
  setOver(true);
1577
1592
  },
1578
- element
1593
+ target
1579
1594
  );
1580
1595
  useEventListener(
1581
1596
  "dragover",
1582
1597
  (event) => {
1583
1598
  event.preventDefault();
1584
1599
  },
1585
- element
1600
+ target
1586
1601
  );
1587
1602
  useEventListener(
1588
1603
  "dragleave",
@@ -1593,7 +1608,7 @@ function useDropZone(target, onDrop) {
1593
1608
  setOver(false);
1594
1609
  }
1595
1610
  },
1596
- element
1611
+ target
1597
1612
  );
1598
1613
  useEventListener(
1599
1614
  "drop",
@@ -1605,7 +1620,7 @@ function useDropZone(target, onDrop) {
1605
1620
  const files = Array.from((_b = (_a = event.dataTransfer) == null ? void 0 : _a.files) != null ? _b : []);
1606
1621
  onDrop == null ? void 0 : onDrop(files.length === 0 ? null : files);
1607
1622
  },
1608
- element
1623
+ target
1609
1624
  );
1610
1625
  return over;
1611
1626
  }
@@ -1785,25 +1800,27 @@ function useInfiniteScroll(target, onLoadMore, options = {}) {
1785
1800
  [direction]: (_b = options.distance) != null ? _b : 0
1786
1801
  }, options.offset)
1787
1802
  }));
1788
- const element = getTargetElement(target);
1803
+ const element = useLatestElement(target);
1804
+ const latestOptions = useLatest(options);
1789
1805
  const di = state[3][direction];
1790
1806
  useUpdateEffect(() => {
1807
+ const opts = latestOptions.current;
1791
1808
  const fn = () => __async$2(this, null, function* () {
1792
- var _a2, _b2;
1809
+ var _a2, _b2, _c, _d;
1793
1810
  const previous = {
1794
- height: (_a2 = element == null ? void 0 : element.scrollHeight) != null ? _a2 : 0,
1795
- width: (_b2 = element == null ? void 0 : element.scrollWidth) != null ? _b2 : 0
1811
+ height: (_b2 = (_a2 = element.current) == null ? void 0 : _a2.scrollHeight) != null ? _b2 : 0,
1812
+ width: (_d = (_c = element.current) == null ? void 0 : _c.scrollWidth) != null ? _d : 0
1796
1813
  };
1797
1814
  yield savedLoadMore.current(state);
1798
- if (options.preserveScrollPosition && element) {
1799
- element.scrollTo({
1800
- top: element.scrollHeight - previous.height,
1801
- left: element.scrollWidth - previous.width
1815
+ if (opts.preserveScrollPosition && element.current) {
1816
+ element.current.scrollTo({
1817
+ top: element.current.scrollHeight - previous.height,
1818
+ left: element.current.scrollWidth - previous.width
1802
1819
  });
1803
1820
  }
1804
1821
  });
1805
1822
  fn();
1806
- }, [di, options.preserveScrollPosition, element]);
1823
+ }, [di]);
1807
1824
  }
1808
1825
 
1809
1826
  const defaultEvents = [
@@ -1840,7 +1857,7 @@ function useMousePressed(target, options = {}) {
1840
1857
  const { touch = true, drag = true, initialValue = false } = options;
1841
1858
  const [pressed, setPressed] = useState(initialValue);
1842
1859
  const [sourceType, setSourceType] = useState(null);
1843
- const element = getTargetElement(target);
1860
+ const elementRef = useLatestElement(target);
1844
1861
  const onPressed = useCallback(
1845
1862
  (srcType) => () => {
1846
1863
  setPressed(true);
@@ -1856,6 +1873,7 @@ function useMousePressed(target, options = {}) {
1856
1873
  useEventListener("mouseleave", onReleased, () => window, { passive: true });
1857
1874
  useEventListener("mouseup", onReleased, () => window, { passive: true });
1858
1875
  useEffect(() => {
1876
+ const element = elementRef.current;
1859
1877
  if (drag) {
1860
1878
  element == null ? void 0 : element.addEventListener("dragstart", onPressed("mouse"), {
1861
1879
  passive: true
@@ -1890,7 +1908,7 @@ function useMousePressed(target, options = {}) {
1890
1908
  element == null ? void 0 : element.removeEventListener("touchcancel", onReleased);
1891
1909
  }
1892
1910
  };
1893
- }, [drag, onPressed, onReleased, element, touch]);
1911
+ }, [drag, onPressed, onReleased, touch, elementRef.current]);
1894
1912
  return [pressed, sourceType];
1895
1913
  }
1896
1914
 
@@ -1907,32 +1925,34 @@ function preventDefault(rawEvent) {
1907
1925
  function useScrollLock(target, initialState = false) {
1908
1926
  const [locked, setLocked] = useState(initialState);
1909
1927
  const initialOverflowRef = useRef("scroll");
1910
- const element = getTargetElement(target);
1928
+ const element = useLatestElement(target);
1911
1929
  useEffect(() => {
1912
- if (element) {
1913
- initialOverflowRef.current = element.style.overflow;
1930
+ if (element.current) {
1931
+ initialOverflowRef.current = element.current.style.overflow;
1914
1932
  if (locked) {
1915
- element.style.overflow = "hidden";
1933
+ element.current.style.overflow = "hidden";
1916
1934
  }
1917
1935
  }
1918
- }, [locked, element]);
1936
+ }, [locked, element.current]);
1919
1937
  const lock = useEvent(() => {
1920
- if (!element || locked) {
1938
+ if (!element.current || locked) {
1921
1939
  return;
1922
1940
  }
1923
1941
  if (isIOS) {
1924
- element.addEventListener("touchmove", preventDefault, { passive: false });
1942
+ element.current.addEventListener("touchmove", preventDefault, {
1943
+ passive: false
1944
+ });
1925
1945
  }
1926
1946
  setLocked(true);
1927
1947
  });
1928
1948
  const unlock = useEvent(() => {
1929
- if (!element || !locked) {
1949
+ if (!element.current || !locked) {
1930
1950
  return;
1931
1951
  }
1932
1952
  if (isIOS) {
1933
- element.removeEventListener("touchmove", preventDefault);
1953
+ element.current.removeEventListener("touchmove", preventDefault);
1934
1954
  }
1935
- element.style.overflow = initialOverflowRef.current;
1955
+ element.current.style.overflow = initialOverflowRef.current;
1936
1956
  setLocked(false);
1937
1957
  });
1938
1958
  const set = useEvent((flag) => {
@@ -2116,14 +2136,15 @@ function useActiveElement() {
2116
2136
 
2117
2137
  function useDraggable(target, options = {}) {
2118
2138
  var _a, _b;
2119
- const draggingElement = getTargetElement(
2120
- options.draggingElement,
2121
- defaultWindow
2122
- );
2123
- const draggingHandle = getTargetElement((_a = options.handle) != null ? _a : target);
2139
+ const draggingElement = options.draggingElement;
2140
+ const draggingHandle = (_a = options.handle) != null ? _a : target;
2124
2141
  const [position, setPositon] = useState(
2125
2142
  (_b = options.initialValue) != null ? _b : { x: 0, y: 0 }
2126
2143
  );
2144
+ useEffect(() => {
2145
+ var _a2;
2146
+ setPositon((_a2 = options.initialValue) != null ? _a2 : { x: 0, y: 0 });
2147
+ }, [options.initialValue]);
2127
2148
  const [pressedDelta, setPressedDelta] = useState();
2128
2149
  const filterEvent = (e) => {
2129
2150
  if (options.pointerTypes) {
@@ -2615,11 +2636,11 @@ function useScrollIntoView({
2615
2636
  cancelAnimationFrame(frameID.current);
2616
2637
  }
2617
2638
  };
2639
+ const element = useLatestElement(targetElement);
2618
2640
  const scrollIntoView = useEvent(
2619
2641
  ({ alignment = "start" } = {}) => {
2620
2642
  var _a;
2621
- const element = getTargetElement(targetElement);
2622
- const parent = getTargetElement(scrollElement) || getScrollParent(axis, element);
2643
+ const parent = getTargetElement(scrollElement) || getScrollParent(axis, element.current);
2623
2644
  shouldStop.current = false;
2624
2645
  if (frameID.current) {
2625
2646
  cancel();
@@ -2627,7 +2648,7 @@ function useScrollIntoView({
2627
2648
  const start = (_a = getScrollStart({ parent, axis })) != null ? _a : 0;
2628
2649
  const change = getRelativePosition({
2629
2650
  parent,
2630
- target: element,
2651
+ target: element.current,
2631
2652
  axis,
2632
2653
  alignment,
2633
2654
  offset,
@@ -2679,13 +2700,12 @@ const useSticky = ({
2679
2700
  nav = 0
2680
2701
  }) => {
2681
2702
  const [isSticky, setSticky] = useState(false);
2682
- const element = getTargetElement(targetElement);
2683
- const scrollParent = getTargetElement(scrollElement) || getScrollParent(axis, element);
2703
+ const element = useLatestElement(targetElement);
2684
2704
  const { run: scrollHandler } = useThrottleFn(() => {
2685
- if (!element) {
2705
+ if (!element.current) {
2686
2706
  return;
2687
2707
  }
2688
- const rect = element.getBoundingClientRect();
2708
+ const rect = element.current.getBoundingClientRect();
2689
2709
  if (axis === "y") {
2690
2710
  setSticky((rect == null ? void 0 : rect.top) <= nav);
2691
2711
  } else {
@@ -2693,7 +2713,8 @@ const useSticky = ({
2693
2713
  }
2694
2714
  }, 50);
2695
2715
  useEffect(() => {
2696
- if (!element || !scrollParent) {
2716
+ const scrollParent = getTargetElement(scrollElement) || getScrollParent(axis, element.current);
2717
+ if (!element.current || !scrollParent) {
2697
2718
  return;
2698
2719
  }
2699
2720
  scrollParent.addEventListener("scroll", scrollHandler);
@@ -2701,7 +2722,7 @@ const useSticky = ({
2701
2722
  return () => {
2702
2723
  scrollParent.removeEventListener("scroll", scrollHandler);
2703
2724
  };
2704
- }, [axis, element, scrollHandler, scrollParent]);
2725
+ }, [axis, element, scrollElement, scrollHandler]);
2705
2726
  return [isSticky, setSticky];
2706
2727
  };
2707
2728
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reactuses/core",
3
- "version": "2.0.2",
3
+ "version": "2.2.1",
4
4
  "main": "./dist/index.cjs",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",