@reactuses/core 6.2.0 → 6.3.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.
- package/dist/index.cjs +115 -67
- package/dist/index.mjs +115 -67
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
3
3
|
var React = require('react');
|
|
4
4
|
var lodashEs = require('lodash-es');
|
|
5
5
|
var Cookies = require('js-cookie');
|
|
6
|
-
var screenfull = require('screenfull');
|
|
7
6
|
var index_js = require('use-sync-external-store/shim/index.js');
|
|
7
|
+
var screenfull = require('screenfull');
|
|
8
8
|
var fetchEventSource = require('@microsoft/fetch-event-source');
|
|
9
9
|
|
|
10
10
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
@@ -292,7 +292,7 @@ const useClickOutside = (target, handler, enabled = true)=>{
|
|
|
292
292
|
useEventListener('touchstart', listener, defaultWindow, listerOptions);
|
|
293
293
|
};
|
|
294
294
|
|
|
295
|
-
function getInitialState$
|
|
295
|
+
function getInitialState$4(key, defaultValue) {
|
|
296
296
|
// Prevent a React hydration mismatch when a default value is provided.
|
|
297
297
|
if (defaultValue !== undefined) {
|
|
298
298
|
return defaultValue;
|
|
@@ -306,7 +306,7 @@ function getInitialState$5(key, defaultValue) {
|
|
|
306
306
|
return '';
|
|
307
307
|
}
|
|
308
308
|
const useCookie = (key, options = defaultOptions$1, defaultValue)=>{
|
|
309
|
-
const [cookieValue, setCookieValue] = React.useState(getInitialState$
|
|
309
|
+
const [cookieValue, setCookieValue] = React.useState(getInitialState$4(key, defaultValue));
|
|
310
310
|
React.useEffect(()=>{
|
|
311
311
|
const getStoredValue = ()=>{
|
|
312
312
|
const raw = Cookies__default.default.get(key);
|
|
@@ -525,7 +525,7 @@ const defaultOptions = {
|
|
|
525
525
|
observe: false
|
|
526
526
|
};
|
|
527
527
|
|
|
528
|
-
function getInitialState$
|
|
528
|
+
function getInitialState$3(defaultValue) {
|
|
529
529
|
// Prevent a React hydration mismatch when a default value is provided.
|
|
530
530
|
if (defaultValue !== undefined) {
|
|
531
531
|
return defaultValue;
|
|
@@ -540,7 +540,7 @@ function getInitialState$4(defaultValue) {
|
|
|
540
540
|
}
|
|
541
541
|
const useCssVar = (prop, target, defaultValue, options = defaultOptions)=>{
|
|
542
542
|
const { observe } = options;
|
|
543
|
-
const [variable, setVariable] = React.useState(getInitialState$
|
|
543
|
+
const [variable, setVariable] = React.useState(getInitialState$3(defaultValue));
|
|
544
544
|
const observerRef = React.useRef();
|
|
545
545
|
const set = React.useCallback((v)=>{
|
|
546
546
|
const element = getTargetElement(target);
|
|
@@ -662,28 +662,6 @@ const StorageSerializers = {
|
|
|
662
662
|
write: (v)=>v.toISOString()
|
|
663
663
|
}
|
|
664
664
|
};
|
|
665
|
-
function getInitialState$3(key, defaultValue, storage, serializer, onError) {
|
|
666
|
-
// Prevent a React hydration mismatch when a default value is provided.
|
|
667
|
-
if (defaultValue !== undefined) {
|
|
668
|
-
return defaultValue;
|
|
669
|
-
}
|
|
670
|
-
if (isBrowser) {
|
|
671
|
-
try {
|
|
672
|
-
const raw = storage == null ? void 0 : storage.getItem(key);
|
|
673
|
-
if (raw !== undefined && raw !== null) {
|
|
674
|
-
return serializer == null ? void 0 : serializer.read(raw);
|
|
675
|
-
}
|
|
676
|
-
return null;
|
|
677
|
-
} catch (error) {
|
|
678
|
-
onError == null ? void 0 : onError(error);
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
// A default value has not been provided, and you are rendering on the server, warn of a possible hydration mismatch when defaulting to false.
|
|
682
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
683
|
-
console.warn('`createStorage` When server side rendering, defaultValue should be defined to prevent a hydration mismatches.');
|
|
684
|
-
}
|
|
685
|
-
return null;
|
|
686
|
-
}
|
|
687
665
|
function useStorage(key, defaultValue, getStorage = ()=>isBrowser ? sessionStorage : undefined, options = defaultOptions$1) {
|
|
688
666
|
let storage;
|
|
689
667
|
const { onError = defaultOnError, effectStorageValue, mountStorageValue, listenToStorageChanges = true } = options;
|
|
@@ -697,65 +675,135 @@ function useStorage(key, defaultValue, getStorage = ()=>isBrowser ? sessionStora
|
|
|
697
675
|
const type = guessSerializerType(defaultValue);
|
|
698
676
|
var _options_serializer;
|
|
699
677
|
const serializerRef = useLatest((_options_serializer = options.serializer) != null ? _options_serializer : StorageSerializers[type]);
|
|
700
|
-
|
|
701
|
-
|
|
678
|
+
// storageRef and defaultValueRef are updated synchronously each render so that
|
|
679
|
+
// the stable getSnapshot/getServerSnapshot closures always read current values.
|
|
680
|
+
const storageRef = React.useRef(storage);
|
|
681
|
+
storageRef.current = storage;
|
|
682
|
+
const defaultValueRef = React.useRef(defaultValue);
|
|
683
|
+
defaultValueRef.current = defaultValue;
|
|
684
|
+
// Cache for referential stability of deserialized values.
|
|
685
|
+
// lastRawRef uses three-state semantics:
|
|
686
|
+
// undefined → no cached value (initial state or after key change) — absent key yields defaultValue
|
|
687
|
+
// null → key was explicitly removed (setState(null) or cross-tab) — absent key yields null
|
|
688
|
+
// string → cached raw string — compared for referential stability
|
|
689
|
+
const lastRawRef = React.useRef(undefined);
|
|
690
|
+
const lastKeyRef = React.useRef(key);
|
|
691
|
+
const lastValueRef = React.useRef(defaultValue != null ? defaultValue : null);
|
|
692
|
+
// Reset per-key caches when the key changes (runs during render, before snapshot).
|
|
693
|
+
if (lastKeyRef.current !== key) {
|
|
694
|
+
lastKeyRef.current = key;
|
|
695
|
+
lastRawRef.current = undefined;
|
|
696
|
+
lastValueRef.current = defaultValue != null ? defaultValue : null;
|
|
697
|
+
}
|
|
698
|
+
// Internal per-instance subscriber callback stored so updateState can notify it.
|
|
699
|
+
const notifyRef = React.useRef(null);
|
|
700
|
+
const getSnapshot = React.useRef(()=>{
|
|
701
|
+
const currentStorage = storageRef.current;
|
|
702
|
+
var _defaultValueRef_current;
|
|
703
|
+
const fallback = (_defaultValueRef_current = defaultValueRef.current) != null ? _defaultValueRef_current : null;
|
|
704
|
+
if (!currentStorage) {
|
|
705
|
+
// Storage unavailable — act as an in-memory state holder using the same
|
|
706
|
+
// three-state lastRawRef semantics so updateState() still works.
|
|
707
|
+
if (lastRawRef.current === undefined) return fallback;
|
|
708
|
+
return lastRawRef.current === null ? null : lastValueRef.current;
|
|
709
|
+
}
|
|
710
|
+
try {
|
|
711
|
+
const raw = currentStorage.getItem(lastKeyRef.current);
|
|
712
|
+
if (raw === null) {
|
|
713
|
+
// lastRawRef === null means the key was explicitly removed; return null.
|
|
714
|
+
// lastRawRef !== null means the key is merely absent (e.g. after key change); return defaultValue.
|
|
715
|
+
return lastRawRef.current === null ? null : fallback;
|
|
716
|
+
}
|
|
717
|
+
if (raw === lastRawRef.current) return lastValueRef.current;
|
|
718
|
+
const deserialized = serializerRef.current.read(raw);
|
|
719
|
+
lastRawRef.current = raw;
|
|
720
|
+
lastValueRef.current = deserialized;
|
|
721
|
+
return deserialized;
|
|
722
|
+
} catch (e) {
|
|
723
|
+
onErrorRef.current(e);
|
|
724
|
+
return fallback;
|
|
725
|
+
}
|
|
726
|
+
}).current;
|
|
727
|
+
const getServerSnapshot = React.useRef(()=>{
|
|
728
|
+
var _defaultValueRef_current;
|
|
729
|
+
return (_defaultValueRef_current = defaultValueRef.current) != null ? _defaultValueRef_current : null;
|
|
730
|
+
}).current;
|
|
731
|
+
// subscribe is stable: it only registers/clears the React-provided callback.
|
|
732
|
+
// Cross-tab listener management is handled separately in a useEffect so that
|
|
733
|
+
// changes to listenToStorageChanges are properly reflected after mount.
|
|
734
|
+
const subscribe = React.useRef((callback)=>{
|
|
735
|
+
notifyRef.current = callback;
|
|
736
|
+
return ()=>{
|
|
737
|
+
notifyRef.current = null;
|
|
738
|
+
};
|
|
739
|
+
}).current;
|
|
740
|
+
const state = index_js.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
741
|
+
// Manage the cross-tab storage listener independently so that toggling
|
|
742
|
+
// listenToStorageChanges after mount correctly adds or removes the listener.
|
|
743
|
+
React.useEffect(()=>{
|
|
744
|
+
if (!listenToStorageChanges || !isBrowser) return;
|
|
745
|
+
const crossTabListener = (e)=>{
|
|
746
|
+
// e.key is null when storage.clear() is called from another tab (Web Storage
|
|
747
|
+
// spec). In that case all keys are affected, so always notify. Otherwise only
|
|
748
|
+
// notify when the event matches the current key.
|
|
749
|
+
// lastKeyRef is updated synchronously during render, so it always holds the
|
|
750
|
+
// latest key at the time this async event fires.
|
|
751
|
+
if (e.key !== null && e.key !== lastKeyRef.current) return;
|
|
752
|
+
// e.newValue is null when the key was removed (removeItem or clear).
|
|
753
|
+
// Update the in-memory caches now so getSnapshot returns null immediately
|
|
754
|
+
// rather than falling back to defaultValue, matching the old behavior where
|
|
755
|
+
// the cross-tab listener called updateState(null) for absent keys.
|
|
756
|
+
if (e.newValue === null) {
|
|
757
|
+
lastRawRef.current = null;
|
|
758
|
+
lastValueRef.current = null;
|
|
759
|
+
}
|
|
760
|
+
notifyRef.current == null ? void 0 : notifyRef.current.call(notifyRef);
|
|
761
|
+
};
|
|
762
|
+
window.addEventListener('storage', crossTabListener);
|
|
763
|
+
return ()=>window.removeEventListener('storage', crossTabListener);
|
|
764
|
+
}, [
|
|
765
|
+
listenToStorageChanges
|
|
766
|
+
]);
|
|
767
|
+
// Write mountStorageValue / defaultValue to storage on mount when key is absent.
|
|
768
|
+
React.useEffect(()=>{
|
|
702
769
|
const serializer = serializerRef.current;
|
|
703
770
|
const storageValue = storageValueRef.current;
|
|
704
771
|
var _ref;
|
|
705
772
|
const data = (_ref = storageValue ? isFunction(storageValue) ? storageValue() : storageValue : defaultValue) != null ? _ref : null;
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
storage == null ? void 0 : storage.setItem(key, serializer.write(data));
|
|
713
|
-
return data;
|
|
714
|
-
}
|
|
715
|
-
} catch (e) {
|
|
716
|
-
onErrorRef.current(e);
|
|
773
|
+
try {
|
|
774
|
+
const raw = storage == null ? void 0 : storage.getItem(key);
|
|
775
|
+
if ((raw === null || raw === undefined) && data !== null) {
|
|
776
|
+
storage == null ? void 0 : storage.setItem(key, serializer.write(data));
|
|
777
|
+
lastRawRef.current = undefined;
|
|
778
|
+
notifyRef.current == null ? void 0 : notifyRef.current.call(notifyRef);
|
|
717
779
|
}
|
|
718
|
-
}
|
|
719
|
-
|
|
780
|
+
} catch (e) {
|
|
781
|
+
onErrorRef.current(e);
|
|
782
|
+
}
|
|
783
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
720
784
|
}, [
|
|
721
785
|
key,
|
|
722
786
|
storage
|
|
723
787
|
]);
|
|
724
788
|
const updateState = useEvent((valOrFunc)=>{
|
|
725
|
-
const currentState = isFunction(valOrFunc) ? valOrFunc(
|
|
726
|
-
setState(currentState);
|
|
789
|
+
const currentState = isFunction(valOrFunc) ? valOrFunc(getSnapshot()) : valOrFunc;
|
|
727
790
|
if (currentState === null) {
|
|
728
791
|
storage == null ? void 0 : storage.removeItem(key);
|
|
792
|
+
lastRawRef.current = null;
|
|
793
|
+
lastValueRef.current = null;
|
|
729
794
|
} else {
|
|
730
795
|
try {
|
|
731
|
-
|
|
796
|
+
const raw = serializerRef.current.write(currentState);
|
|
797
|
+
storage == null ? void 0 : storage.setItem(key, raw);
|
|
798
|
+
lastRawRef.current = raw;
|
|
799
|
+
lastValueRef.current = currentState;
|
|
732
800
|
} catch (e) {
|
|
733
801
|
onErrorRef.current(e);
|
|
802
|
+
return;
|
|
734
803
|
}
|
|
735
804
|
}
|
|
805
|
+
notifyRef.current == null ? void 0 : notifyRef.current.call(notifyRef);
|
|
736
806
|
});
|
|
737
|
-
const listener = useEvent(()=>{
|
|
738
|
-
try {
|
|
739
|
-
const raw = storage == null ? void 0 : storage.getItem(key);
|
|
740
|
-
if (raw !== undefined && raw !== null) {
|
|
741
|
-
updateState(serializerRef.current.read(raw));
|
|
742
|
-
} else {
|
|
743
|
-
updateState(null);
|
|
744
|
-
}
|
|
745
|
-
} catch (e) {
|
|
746
|
-
onErrorRef.current(e);
|
|
747
|
-
}
|
|
748
|
-
});
|
|
749
|
-
React.useEffect(()=>{
|
|
750
|
-
if (listenToStorageChanges) {
|
|
751
|
-
window.addEventListener('storage', listener);
|
|
752
|
-
return ()=>window.removeEventListener('storage', listener);
|
|
753
|
-
}
|
|
754
|
-
return ()=>{};
|
|
755
|
-
}, [
|
|
756
|
-
listenToStorageChanges,
|
|
757
|
-
listener
|
|
758
|
-
]);
|
|
759
807
|
return [
|
|
760
808
|
state,
|
|
761
809
|
updateState
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React, { useLayoutEffect, useEffect, useRef, useReducer, useState, useCallback, useMemo } from 'react';
|
|
2
2
|
import { isEqual, debounce, throttle } from 'lodash-es';
|
|
3
3
|
import Cookies from 'js-cookie';
|
|
4
|
-
import screenfull from 'screenfull';
|
|
5
4
|
import { useSyncExternalStore } from 'use-sync-external-store/shim/index.js';
|
|
5
|
+
import screenfull from 'screenfull';
|
|
6
6
|
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
|
7
7
|
|
|
8
8
|
var _window_navigator, _window;
|
|
@@ -284,7 +284,7 @@ const useClickOutside = (target, handler, enabled = true)=>{
|
|
|
284
284
|
useEventListener('touchstart', listener, defaultWindow, listerOptions);
|
|
285
285
|
};
|
|
286
286
|
|
|
287
|
-
function getInitialState$
|
|
287
|
+
function getInitialState$4(key, defaultValue) {
|
|
288
288
|
// Prevent a React hydration mismatch when a default value is provided.
|
|
289
289
|
if (defaultValue !== undefined) {
|
|
290
290
|
return defaultValue;
|
|
@@ -298,7 +298,7 @@ function getInitialState$5(key, defaultValue) {
|
|
|
298
298
|
return '';
|
|
299
299
|
}
|
|
300
300
|
const useCookie = (key, options = defaultOptions$1, defaultValue)=>{
|
|
301
|
-
const [cookieValue, setCookieValue] = useState(getInitialState$
|
|
301
|
+
const [cookieValue, setCookieValue] = useState(getInitialState$4(key, defaultValue));
|
|
302
302
|
useEffect(()=>{
|
|
303
303
|
const getStoredValue = ()=>{
|
|
304
304
|
const raw = Cookies.get(key);
|
|
@@ -517,7 +517,7 @@ const defaultOptions = {
|
|
|
517
517
|
observe: false
|
|
518
518
|
};
|
|
519
519
|
|
|
520
|
-
function getInitialState$
|
|
520
|
+
function getInitialState$3(defaultValue) {
|
|
521
521
|
// Prevent a React hydration mismatch when a default value is provided.
|
|
522
522
|
if (defaultValue !== undefined) {
|
|
523
523
|
return defaultValue;
|
|
@@ -532,7 +532,7 @@ function getInitialState$4(defaultValue) {
|
|
|
532
532
|
}
|
|
533
533
|
const useCssVar = (prop, target, defaultValue, options = defaultOptions)=>{
|
|
534
534
|
const { observe } = options;
|
|
535
|
-
const [variable, setVariable] = useState(getInitialState$
|
|
535
|
+
const [variable, setVariable] = useState(getInitialState$3(defaultValue));
|
|
536
536
|
const observerRef = useRef();
|
|
537
537
|
const set = useCallback((v)=>{
|
|
538
538
|
const element = getTargetElement(target);
|
|
@@ -654,28 +654,6 @@ const StorageSerializers = {
|
|
|
654
654
|
write: (v)=>v.toISOString()
|
|
655
655
|
}
|
|
656
656
|
};
|
|
657
|
-
function getInitialState$3(key, defaultValue, storage, serializer, onError) {
|
|
658
|
-
// Prevent a React hydration mismatch when a default value is provided.
|
|
659
|
-
if (defaultValue !== undefined) {
|
|
660
|
-
return defaultValue;
|
|
661
|
-
}
|
|
662
|
-
if (isBrowser) {
|
|
663
|
-
try {
|
|
664
|
-
const raw = storage == null ? void 0 : storage.getItem(key);
|
|
665
|
-
if (raw !== undefined && raw !== null) {
|
|
666
|
-
return serializer == null ? void 0 : serializer.read(raw);
|
|
667
|
-
}
|
|
668
|
-
return null;
|
|
669
|
-
} catch (error) {
|
|
670
|
-
onError == null ? void 0 : onError(error);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
// A default value has not been provided, and you are rendering on the server, warn of a possible hydration mismatch when defaulting to false.
|
|
674
|
-
if (process.env.NODE_ENV !== 'production') {
|
|
675
|
-
console.warn('`createStorage` When server side rendering, defaultValue should be defined to prevent a hydration mismatches.');
|
|
676
|
-
}
|
|
677
|
-
return null;
|
|
678
|
-
}
|
|
679
657
|
function useStorage(key, defaultValue, getStorage = ()=>isBrowser ? sessionStorage : undefined, options = defaultOptions$1) {
|
|
680
658
|
let storage;
|
|
681
659
|
const { onError = defaultOnError, effectStorageValue, mountStorageValue, listenToStorageChanges = true } = options;
|
|
@@ -689,65 +667,135 @@ function useStorage(key, defaultValue, getStorage = ()=>isBrowser ? sessionStora
|
|
|
689
667
|
const type = guessSerializerType(defaultValue);
|
|
690
668
|
var _options_serializer;
|
|
691
669
|
const serializerRef = useLatest((_options_serializer = options.serializer) != null ? _options_serializer : StorageSerializers[type]);
|
|
692
|
-
|
|
693
|
-
|
|
670
|
+
// storageRef and defaultValueRef are updated synchronously each render so that
|
|
671
|
+
// the stable getSnapshot/getServerSnapshot closures always read current values.
|
|
672
|
+
const storageRef = useRef(storage);
|
|
673
|
+
storageRef.current = storage;
|
|
674
|
+
const defaultValueRef = useRef(defaultValue);
|
|
675
|
+
defaultValueRef.current = defaultValue;
|
|
676
|
+
// Cache for referential stability of deserialized values.
|
|
677
|
+
// lastRawRef uses three-state semantics:
|
|
678
|
+
// undefined → no cached value (initial state or after key change) — absent key yields defaultValue
|
|
679
|
+
// null → key was explicitly removed (setState(null) or cross-tab) — absent key yields null
|
|
680
|
+
// string → cached raw string — compared for referential stability
|
|
681
|
+
const lastRawRef = useRef(undefined);
|
|
682
|
+
const lastKeyRef = useRef(key);
|
|
683
|
+
const lastValueRef = useRef(defaultValue != null ? defaultValue : null);
|
|
684
|
+
// Reset per-key caches when the key changes (runs during render, before snapshot).
|
|
685
|
+
if (lastKeyRef.current !== key) {
|
|
686
|
+
lastKeyRef.current = key;
|
|
687
|
+
lastRawRef.current = undefined;
|
|
688
|
+
lastValueRef.current = defaultValue != null ? defaultValue : null;
|
|
689
|
+
}
|
|
690
|
+
// Internal per-instance subscriber callback stored so updateState can notify it.
|
|
691
|
+
const notifyRef = useRef(null);
|
|
692
|
+
const getSnapshot = useRef(()=>{
|
|
693
|
+
const currentStorage = storageRef.current;
|
|
694
|
+
var _defaultValueRef_current;
|
|
695
|
+
const fallback = (_defaultValueRef_current = defaultValueRef.current) != null ? _defaultValueRef_current : null;
|
|
696
|
+
if (!currentStorage) {
|
|
697
|
+
// Storage unavailable — act as an in-memory state holder using the same
|
|
698
|
+
// three-state lastRawRef semantics so updateState() still works.
|
|
699
|
+
if (lastRawRef.current === undefined) return fallback;
|
|
700
|
+
return lastRawRef.current === null ? null : lastValueRef.current;
|
|
701
|
+
}
|
|
702
|
+
try {
|
|
703
|
+
const raw = currentStorage.getItem(lastKeyRef.current);
|
|
704
|
+
if (raw === null) {
|
|
705
|
+
// lastRawRef === null means the key was explicitly removed; return null.
|
|
706
|
+
// lastRawRef !== null means the key is merely absent (e.g. after key change); return defaultValue.
|
|
707
|
+
return lastRawRef.current === null ? null : fallback;
|
|
708
|
+
}
|
|
709
|
+
if (raw === lastRawRef.current) return lastValueRef.current;
|
|
710
|
+
const deserialized = serializerRef.current.read(raw);
|
|
711
|
+
lastRawRef.current = raw;
|
|
712
|
+
lastValueRef.current = deserialized;
|
|
713
|
+
return deserialized;
|
|
714
|
+
} catch (e) {
|
|
715
|
+
onErrorRef.current(e);
|
|
716
|
+
return fallback;
|
|
717
|
+
}
|
|
718
|
+
}).current;
|
|
719
|
+
const getServerSnapshot = useRef(()=>{
|
|
720
|
+
var _defaultValueRef_current;
|
|
721
|
+
return (_defaultValueRef_current = defaultValueRef.current) != null ? _defaultValueRef_current : null;
|
|
722
|
+
}).current;
|
|
723
|
+
// subscribe is stable: it only registers/clears the React-provided callback.
|
|
724
|
+
// Cross-tab listener management is handled separately in a useEffect so that
|
|
725
|
+
// changes to listenToStorageChanges are properly reflected after mount.
|
|
726
|
+
const subscribe = useRef((callback)=>{
|
|
727
|
+
notifyRef.current = callback;
|
|
728
|
+
return ()=>{
|
|
729
|
+
notifyRef.current = null;
|
|
730
|
+
};
|
|
731
|
+
}).current;
|
|
732
|
+
const state = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
733
|
+
// Manage the cross-tab storage listener independently so that toggling
|
|
734
|
+
// listenToStorageChanges after mount correctly adds or removes the listener.
|
|
735
|
+
useEffect(()=>{
|
|
736
|
+
if (!listenToStorageChanges || !isBrowser) return;
|
|
737
|
+
const crossTabListener = (e)=>{
|
|
738
|
+
// e.key is null when storage.clear() is called from another tab (Web Storage
|
|
739
|
+
// spec). In that case all keys are affected, so always notify. Otherwise only
|
|
740
|
+
// notify when the event matches the current key.
|
|
741
|
+
// lastKeyRef is updated synchronously during render, so it always holds the
|
|
742
|
+
// latest key at the time this async event fires.
|
|
743
|
+
if (e.key !== null && e.key !== lastKeyRef.current) return;
|
|
744
|
+
// e.newValue is null when the key was removed (removeItem or clear).
|
|
745
|
+
// Update the in-memory caches now so getSnapshot returns null immediately
|
|
746
|
+
// rather than falling back to defaultValue, matching the old behavior where
|
|
747
|
+
// the cross-tab listener called updateState(null) for absent keys.
|
|
748
|
+
if (e.newValue === null) {
|
|
749
|
+
lastRawRef.current = null;
|
|
750
|
+
lastValueRef.current = null;
|
|
751
|
+
}
|
|
752
|
+
notifyRef.current == null ? void 0 : notifyRef.current.call(notifyRef);
|
|
753
|
+
};
|
|
754
|
+
window.addEventListener('storage', crossTabListener);
|
|
755
|
+
return ()=>window.removeEventListener('storage', crossTabListener);
|
|
756
|
+
}, [
|
|
757
|
+
listenToStorageChanges
|
|
758
|
+
]);
|
|
759
|
+
// Write mountStorageValue / defaultValue to storage on mount when key is absent.
|
|
760
|
+
useEffect(()=>{
|
|
694
761
|
const serializer = serializerRef.current;
|
|
695
762
|
const storageValue = storageValueRef.current;
|
|
696
763
|
var _ref;
|
|
697
764
|
const data = (_ref = storageValue ? isFunction(storageValue) ? storageValue() : storageValue : defaultValue) != null ? _ref : null;
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
storage == null ? void 0 : storage.setItem(key, serializer.write(data));
|
|
705
|
-
return data;
|
|
706
|
-
}
|
|
707
|
-
} catch (e) {
|
|
708
|
-
onErrorRef.current(e);
|
|
765
|
+
try {
|
|
766
|
+
const raw = storage == null ? void 0 : storage.getItem(key);
|
|
767
|
+
if ((raw === null || raw === undefined) && data !== null) {
|
|
768
|
+
storage == null ? void 0 : storage.setItem(key, serializer.write(data));
|
|
769
|
+
lastRawRef.current = undefined;
|
|
770
|
+
notifyRef.current == null ? void 0 : notifyRef.current.call(notifyRef);
|
|
709
771
|
}
|
|
710
|
-
}
|
|
711
|
-
|
|
772
|
+
} catch (e) {
|
|
773
|
+
onErrorRef.current(e);
|
|
774
|
+
}
|
|
775
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
712
776
|
}, [
|
|
713
777
|
key,
|
|
714
778
|
storage
|
|
715
779
|
]);
|
|
716
780
|
const updateState = useEvent((valOrFunc)=>{
|
|
717
|
-
const currentState = isFunction(valOrFunc) ? valOrFunc(
|
|
718
|
-
setState(currentState);
|
|
781
|
+
const currentState = isFunction(valOrFunc) ? valOrFunc(getSnapshot()) : valOrFunc;
|
|
719
782
|
if (currentState === null) {
|
|
720
783
|
storage == null ? void 0 : storage.removeItem(key);
|
|
784
|
+
lastRawRef.current = null;
|
|
785
|
+
lastValueRef.current = null;
|
|
721
786
|
} else {
|
|
722
787
|
try {
|
|
723
|
-
|
|
788
|
+
const raw = serializerRef.current.write(currentState);
|
|
789
|
+
storage == null ? void 0 : storage.setItem(key, raw);
|
|
790
|
+
lastRawRef.current = raw;
|
|
791
|
+
lastValueRef.current = currentState;
|
|
724
792
|
} catch (e) {
|
|
725
793
|
onErrorRef.current(e);
|
|
794
|
+
return;
|
|
726
795
|
}
|
|
727
796
|
}
|
|
797
|
+
notifyRef.current == null ? void 0 : notifyRef.current.call(notifyRef);
|
|
728
798
|
});
|
|
729
|
-
const listener = useEvent(()=>{
|
|
730
|
-
try {
|
|
731
|
-
const raw = storage == null ? void 0 : storage.getItem(key);
|
|
732
|
-
if (raw !== undefined && raw !== null) {
|
|
733
|
-
updateState(serializerRef.current.read(raw));
|
|
734
|
-
} else {
|
|
735
|
-
updateState(null);
|
|
736
|
-
}
|
|
737
|
-
} catch (e) {
|
|
738
|
-
onErrorRef.current(e);
|
|
739
|
-
}
|
|
740
|
-
});
|
|
741
|
-
useEffect(()=>{
|
|
742
|
-
if (listenToStorageChanges) {
|
|
743
|
-
window.addEventListener('storage', listener);
|
|
744
|
-
return ()=>window.removeEventListener('storage', listener);
|
|
745
|
-
}
|
|
746
|
-
return ()=>{};
|
|
747
|
-
}, [
|
|
748
|
-
listenToStorageChanges,
|
|
749
|
-
listener
|
|
750
|
-
]);
|
|
751
799
|
return [
|
|
752
800
|
state,
|
|
753
801
|
updateState
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reactuses/core",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.3.0",
|
|
4
4
|
"description": "Collection of 100+ essential React Hooks with TypeScript support, tree-shaking, and SSR compatibility. Sensors, browser APIs, state management, animations, and more.",
|
|
5
5
|
"license": "Unlicense",
|
|
6
6
|
"homepage": "https://www.reactuse.com/",
|