@wener/console
Version:
Base console UI toolkit
256 lines (255 loc) • 10.3 kB
JavaScript
// https://github.com/astoilkov/use-local-storage-state/blob/main/src/useLocalStorageState.ts
function _array_like_to_array(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
return arr2;
}
function _array_with_holes(arr) {
if (Array.isArray(arr)) return arr;
}
function _array_without_holes(arr) {
if (Array.isArray(arr)) return _array_like_to_array(arr);
}
function _instanceof(left, right) {
if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) {
return !!right[Symbol.hasInstance](left);
} else {
return left instanceof right;
}
}
function _iterable_to_array(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _iterable_to_array_limit(arr, i) {
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
if (_i == null) return;
var _arr = [];
var _n = true;
var _d = false;
var _s, _e;
try {
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally{
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally{
if (_d) throw _e;
}
}
return _arr;
}
function _non_iterable_rest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _non_iterable_spread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _sliced_to_array(arr, i) {
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
}
function _to_consumable_array(arr) {
return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread();
}
function _unsupported_iterable_to_array(o, minLen) {
if (!o) return;
if (typeof o === "string") return _array_like_to_array(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(n);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
}
import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';
// in memory fallback used then `localStorage` throws an error
export var inMemoryData = new Map();
export function useLocalStorageState(key, options) {
var _useState = _sliced_to_array(useState(options === null || options === void 0 ? void 0 : options.defaultValue), 1), defaultValue = _useState[0];
// SSR support
// - on the server, return a constant value
// - this makes the implementation simpler and smaller because the `localStorage` object is
// `undefined` on the server
if (typeof window === 'undefined') {
return [
defaultValue,
function() {},
{
isPersistent: true,
removeItem: function() {}
}
];
}
var serializer = options === null || options === void 0 ? void 0 : options.serializer;
// disabling ESLint because the above if statement can be executed only on the server. the value
// of `window` can't change between calls.
// eslint-disable-next-line react-hooks/rules-of-hooks
return useBrowserLocalStorageState(key, defaultValue, options === null || options === void 0 ? void 0 : options.storageSync, serializer === null || serializer === void 0 ? void 0 : serializer.parse, serializer === null || serializer === void 0 ? void 0 : serializer.stringify);
}
function useBrowserLocalStorageState(key, defaultValue) {
var storageSync = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : true, parse = arguments.length > 3 && arguments[3] !== void 0 ? arguments[3] : parseJSON, stringify = arguments.length > 4 && arguments[4] !== void 0 ? arguments[4] : JSON.stringify;
// store default value in localStorage:
// - initial issue: https://github.com/astoilkov/use-local-storage-state/issues/26
// issues that were caused by incorrect initial and secondary implementations:
// - https://github.com/astoilkov/use-local-storage-state/issues/30
// - https://github.com/astoilkov/use-local-storage-state/issues/33
if (!inMemoryData.has(key) && defaultValue !== undefined && goodTry(function() {
return localStorage.getItem(key);
}) === null) {
// reasons for `localStorage` to throw an error:
// - maximum quota is exceeded
// - under Mobile Safari (since iOS 5) when the user enters private mode
// `localStorage.setItem()` will throw
// - trying to access localStorage object when cookies are disabled in Safari throws
// "SecurityError: The operation is insecure."
goodTry(function() {
return localStorage.setItem(key, stringify(defaultValue));
});
}
// we keep the `parsed` value in a ref because `useSyncExternalStore` requires a cached version
var storageValue = useRef({
item: null,
parsed: defaultValue
});
var value = useSyncExternalStore(useCallback(function(onStoreChange) {
var onChange = function(localKey) {
if (key === localKey) {
onStoreChange();
}
};
callbacks.add(onChange);
return function() {
callbacks.delete(onChange);
};
}, [
key
]), // eslint-disable-next-line react-hooks/exhaustive-deps
function() {
var _goodTry;
var item = (_goodTry = goodTry(function() {
return localStorage.getItem(key);
})) !== null && _goodTry !== void 0 ? _goodTry : null;
if (inMemoryData.has(key)) {
storageValue.current = {
item: item,
parsed: inMemoryData.get(key)
};
} else if (item !== storageValue.current.item) {
var parsed;
try {
parsed = item === null ? defaultValue : parse(item);
} catch (unused) {
parsed = defaultValue;
}
storageValue.current = {
item: item,
parsed: parsed
};
}
return storageValue.current.parsed;
}, // istanbul ignore next
function() {
return defaultValue;
});
var setState = useCallback(function(newValue) {
var value = _instanceof(newValue, Function) ? newValue(storageValue.current.parsed) : newValue;
// reasons for `localStorage` to throw an error:
// - maximum quota is exceeded
// - under Mobile Safari (since iOS 5) when the user enters private mode
// `localStorage.setItem()` will throw
// - trying to access `localStorage` object when cookies are disabled in Safari throws
// "SecurityError: The operation is insecure."
try {
localStorage.setItem(key, stringify(value));
inMemoryData.delete(key);
} catch (unused) {
inMemoryData.set(key, value);
}
triggerCallbacks(key);
}, [
key,
stringify
]);
// - syncs change across tabs, windows, iframes
// - the `storage` event is called only in all tabs, windows, iframe's except the one that
// triggered the change
useEffect(function() {
if (!storageSync) {
return undefined;
}
var onStorage = function(e) {
if (e.storageArea === goodTry(function() {
return localStorage;
}) && e.key === key) {
triggerCallbacks(key);
}
};
window.addEventListener('storage', onStorage);
return function() {
return window.removeEventListener('storage', onStorage);
};
}, [
key,
storageSync
]);
return useMemo(function() {
return [
value,
setState,
{
isPersistent: value === defaultValue || !inMemoryData.has(key),
removeItem: function removeItem() {
goodTry(function() {
return localStorage.removeItem(key);
});
inMemoryData.delete(key);
triggerCallbacks(key);
}
}
];
}, [
key,
setState,
value,
defaultValue
]);
}
// notifies all instances using the same `key` to update
var callbacks = new Set();
function triggerCallbacks(key) {
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
try {
for(var _iterator = _to_consumable_array(callbacks)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
var callback = _step.value;
callback(key);
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally{
try {
if (!_iteratorNormalCompletion && _iterator.return != null) {
_iterator.return();
}
} finally{
if (_didIteratorError) {
throw _iteratorError;
}
}
}
}
// a wrapper for `JSON.parse()` that supports "undefined" value. otherwise,
// `JSON.parse(JSON.stringify(undefined))` returns the string "undefined" not the value `undefined`
function parseJSON(value) {
return value === 'undefined' ? undefined : JSON.parse(value);
}
function goodTry(tryFn) {
try {
return tryFn();
} catch (unused) {
return undefined;
}
}