UNPKG

jotai

Version:

👻 Primitive and flexible state management for React

661 lines (656 loc) • 21.8 kB
'use strict'; var keyCount = 0; function atom(read, write) { var key = "atom" + ++keyCount; var config = { toString: function toString() { return key; } }; if (typeof read === 'function') { config.read = read; } else { config.init = read; config.read = function (get) { return get(config); }; config.write = function (get, set, arg) { return set(config, typeof arg === 'function' ? arg(get(config)) : arg); }; } if (write) { config.write = write; } return config; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(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(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(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 _createForOfIteratorHelperLoose(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (it) return (it = it.call(o)).next.bind(it); if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; return function () { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var hasInitialValue = function hasInitialValue(atom) { return 'init' in atom; }; var isActuallyWritableAtom = function isActuallyWritableAtom(atom) { return !!atom.write; }; var cancelPromiseMap = new WeakMap(); var registerCancelPromise = function registerCancelPromise(promise, cancel) { cancelPromiseMap.set(promise, cancel); promise.catch(function () {}).finally(function () { return cancelPromiseMap.delete(promise); }); }; var cancelPromise = function cancelPromise(promise, next) { var cancel = cancelPromiseMap.get(promise); if (cancel) { cancelPromiseMap.delete(promise); cancel(next); } }; var resolvePromise = function resolvePromise(promise, value) { promise.status = 'fulfilled'; promise.value = value; }; var rejectPromise = function rejectPromise(promise, e) { promise.status = 'rejected'; promise.reason = e; }; var isPromiseLike = function isPromiseLike(x) { return typeof (x == null ? void 0 : x.then) === 'function'; }; var isEqualAtomValue = function isEqualAtomValue(a, b) { return 'v' in a && 'v' in b && Object.is(a.v, b.v); }; var isEqualAtomError = function isEqualAtomError(a, b) { return 'e' in a && 'e' in b && Object.is(a.e, b.e); }; var hasPromiseAtomValue = function hasPromiseAtomValue(a) { return 'v' in a && a.v instanceof Promise; }; var isEqualPromiseAtomValue = function isEqualPromiseAtomValue(a, b) { return 'v' in a && 'v' in b && a.v.orig && a.v.orig === b.v.orig; }; var returnAtomValue = function returnAtomValue(atomState) { if ('e' in atomState) { throw atomState.e; } return atomState.v; }; var createStore = function createStore() { var atomStateMap = new WeakMap(); var mountedMap = new WeakMap(); var pendingMap = new Map(); var storeListenersRev1; var storeListenersRev2; var mountedAtoms; if (process.env.NODE_ENV !== 'production') { storeListenersRev1 = new Set(); storeListenersRev2 = new Set(); mountedAtoms = new Set(); } var getAtomState = function getAtomState(atom) { return atomStateMap.get(atom); }; var setAtomState = function setAtomState(atom, atomState) { if (process.env.NODE_ENV !== 'production') { Object.freeze(atomState); } var prevAtomState = atomStateMap.get(atom); atomStateMap.set(atom, atomState); if (!pendingMap.has(atom)) { pendingMap.set(atom, prevAtomState); } if (prevAtomState && hasPromiseAtomValue(prevAtomState)) { var _next = 'v' in atomState ? atomState.v instanceof Promise ? atomState.v : Promise.resolve(atomState.v) : Promise.reject(atomState.e); cancelPromise(prevAtomState.v, _next); } }; var updateDependencies = function updateDependencies(atom, nextAtomState, nextDependencies) { var dependencies = new Map(); var changed = false; nextDependencies.forEach(function (aState, a) { if (!aState && a === atom) { aState = nextAtomState; } if (aState) { dependencies.set(a, aState); if (nextAtomState.d.get(a) !== aState) { changed = true; } } else if (process.env.NODE_ENV !== 'production') { console.warn('[Bug] atom state not found'); } }); if (changed || nextAtomState.d.size !== dependencies.size) { nextAtomState.d = dependencies; } }; var setAtomValue = function setAtomValue(atom, value, nextDependencies) { var prevAtomState = getAtomState(atom); var nextAtomState = { d: (prevAtomState == null ? void 0 : prevAtomState.d) || new Map(), v: value }; if (nextDependencies) { updateDependencies(atom, nextAtomState, nextDependencies); } if (prevAtomState && isEqualAtomValue(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) { return prevAtomState; } if (prevAtomState && hasPromiseAtomValue(prevAtomState) && hasPromiseAtomValue(nextAtomState) && isEqualPromiseAtomValue(prevAtomState, nextAtomState)) { if (prevAtomState.d === nextAtomState.d) { return prevAtomState; } else { nextAtomState.v = prevAtomState.v; } } setAtomState(atom, nextAtomState); return nextAtomState; }; var setAtomValueOrPromise = function setAtomValueOrPromise(atom, valueOrPromise, nextDependencies, abortPromise) { if (isPromiseLike(valueOrPromise)) { var continuePromise; var promise = new Promise(function (resolve, reject) { var settled = false; valueOrPromise.then(function (v) { if (!settled) { settled = true; var prevAtomState = getAtomState(atom); var nextAtomState = setAtomValue(atom, promise, nextDependencies); resolvePromise(promise, v); resolve(v); if ((prevAtomState == null ? void 0 : prevAtomState.d) !== nextAtomState.d) { mountDependencies(atom, nextAtomState, prevAtomState == null ? void 0 : prevAtomState.d); } } }, function (e) { if (!settled) { settled = true; var prevAtomState = getAtomState(atom); var nextAtomState = setAtomValue(atom, promise, nextDependencies); rejectPromise(promise, e); reject(e); if ((prevAtomState == null ? void 0 : prevAtomState.d) !== nextAtomState.d) { mountDependencies(atom, nextAtomState, prevAtomState == null ? void 0 : prevAtomState.d); } } }); continuePromise = function continuePromise(next) { if (!settled) { settled = true; next.then(function (v) { return resolvePromise(promise, v); }, function (e) { return rejectPromise(promise, e); }); resolve(next); } }; }); promise.orig = valueOrPromise; promise.status = 'pending'; registerCancelPromise(promise, function (next) { if (next) { continuePromise(next); } abortPromise == null ? void 0 : abortPromise(); }); return setAtomValue(atom, promise, nextDependencies); } return setAtomValue(atom, valueOrPromise, nextDependencies); }; var setAtomError = function setAtomError(atom, error, nextDependencies) { var prevAtomState = getAtomState(atom); var nextAtomState = { d: (prevAtomState == null ? void 0 : prevAtomState.d) || new Map(), e: error }; if (nextDependencies) { updateDependencies(atom, nextAtomState, nextDependencies); } if (prevAtomState && isEqualAtomError(prevAtomState, nextAtomState) && prevAtomState.d === nextAtomState.d) { return prevAtomState; } setAtomState(atom, nextAtomState); return nextAtomState; }; var readAtomState = function readAtomState(atom) { var atomState = getAtomState(atom); if (atomState) { atomState.d.forEach(function (_, a) { if (a !== atom && !mountedMap.has(a)) { readAtomState(a); } }); if (Array.from(atomState.d).every(function (_ref) { var a = _ref[0], s = _ref[1]; return a === atom || getAtomState(a) === s; })) { return atomState; } } var nextDependencies = new Map(); var isSync = true; var getter = function getter(a) { if (a === atom) { var _aState = getAtomState(a); if (_aState) { nextDependencies.set(a, _aState); return returnAtomValue(_aState); } if (hasInitialValue(a)) { nextDependencies.set(a, undefined); return a.init; } throw new Error('no atom init'); } var aState = readAtomState(a); nextDependencies.set(a, aState); return returnAtomValue(aState); }; var controller; var setSelf; var options = { get signal() { if (!controller) { controller = new AbortController(); } return controller.signal; }, get setSelf() { if (process.env.NODE_ENV !== 'production' && !isActuallyWritableAtom(atom)) { console.warn('setSelf function cannot be used with read-only atom'); } if (!setSelf && isActuallyWritableAtom(atom)) { setSelf = function setSelf() { if (process.env.NODE_ENV !== 'production' && isSync) { console.warn('setSelf function cannot be called in sync'); } if (!isSync) { for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { args[_key] = arguments[_key]; } return writeAtom.apply(void 0, [atom].concat(args)); } }; } return setSelf; } }; try { var valueOrPromise = atom.read(getter, options); return setAtomValueOrPromise(atom, valueOrPromise, nextDependencies, function () { var _controller; return (_controller = controller) == null ? void 0 : _controller.abort(); }); } catch (error) { return setAtomError(atom, error, nextDependencies); } finally { isSync = false; } }; var readAtom = function readAtom(atom) { return returnAtomValue(readAtomState(atom)); }; var addAtom = function addAtom(atom) { var mounted = mountedMap.get(atom); if (!mounted) { mounted = mountAtom(atom); } return mounted; }; var canUnmountAtom = function canUnmountAtom(atom, mounted) { return !mounted.l.size && (!mounted.t.size || mounted.t.size === 1 && mounted.t.has(atom)); }; var delAtom = function delAtom(atom) { var mounted = mountedMap.get(atom); if (mounted && canUnmountAtom(atom, mounted)) { unmountAtom(atom); } }; var recomputeDependents = function recomputeDependents(atom) { var dependencyMap = new Map(); var dirtyMap = new WeakMap(); var loop1 = function loop1(a) { var mounted = mountedMap.get(a); mounted == null ? void 0 : mounted.t.forEach(function (dependent) { if (dependent !== a) { dependencyMap.set(dependent, (dependencyMap.get(dependent) || new Set()).add(a)); dirtyMap.set(dependent, (dirtyMap.get(dependent) || 0) + 1); loop1(dependent); } }); }; loop1(atom); var loop2 = function loop2(a) { var mounted = mountedMap.get(a); mounted == null ? void 0 : mounted.t.forEach(function (dependent) { if (dependent !== a) { var dirtyCount = dirtyMap.get(dependent); if (dirtyCount) { dirtyMap.set(dependent, --dirtyCount); } if (!dirtyCount) { var _dependencyMap$get; var isChanged = !!((_dependencyMap$get = dependencyMap.get(dependent)) != null && _dependencyMap$get.size); if (isChanged) { var prevAtomState = getAtomState(dependent); var nextAtomState = readAtomState(dependent); isChanged = !prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState); } if (!isChanged) { dependencyMap.forEach(function (s) { return s.delete(dependent); }); } } loop2(dependent); } }); }; loop2(atom); }; var writeAtomState = function writeAtomState(atom) { var isSync = true; var getter = function getter(a) { return returnAtomValue(readAtomState(a)); }; var setter = function setter(a) { var r; for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { args[_key3 - 1] = arguments[_key3]; } if (a === atom) { if (!hasInitialValue(a)) { throw new Error('atom not writable'); } var prevAtomState = getAtomState(a); var nextAtomState = setAtomValueOrPromise(a, args[0]); if (!prevAtomState || !isEqualAtomValue(prevAtomState, nextAtomState)) { recomputeDependents(a); } } else { r = writeAtomState.apply(void 0, [a].concat(args)); } if (!isSync) { var flushed = flushPending(); if (process.env.NODE_ENV !== 'production') { storeListenersRev2.forEach(function (l) { return l({ type: 'async-write', flushed: flushed }); }); } } return r; }; for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } var result = atom.write.apply(atom, [getter, setter].concat(args)); isSync = false; return result; }; var writeAtom = function writeAtom(atom) { for (var _len4 = arguments.length, args = new Array(_len4 > 1 ? _len4 - 1 : 0), _key4 = 1; _key4 < _len4; _key4++) { args[_key4 - 1] = arguments[_key4]; } var result = writeAtomState.apply(void 0, [atom].concat(args)); var flushed = flushPending(); if (process.env.NODE_ENV !== 'production') { storeListenersRev2.forEach(function (l) { return l({ type: 'write', flushed: flushed }); }); } return result; }; var mountAtom = function mountAtom(atom, initialDependent) { var mounted = { t: new Set(initialDependent && [initialDependent]), l: new Set() }; mountedMap.set(atom, mounted); if (process.env.NODE_ENV !== 'production') { mountedAtoms.add(atom); } readAtomState(atom).d.forEach(function (_, a) { var aMounted = mountedMap.get(a); if (aMounted) { aMounted.t.add(atom); } else { if (a !== atom) { mountAtom(a, atom); } } }); readAtomState(atom); if (isActuallyWritableAtom(atom) && atom.onMount) { var onUnmount = atom.onMount(function () { for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { args[_key5] = arguments[_key5]; } return writeAtom.apply(void 0, [atom].concat(args)); }); if (onUnmount) { mounted.u = onUnmount; } } return mounted; }; var unmountAtom = function unmountAtom(atom) { var _mountedMap$get; var onUnmount = (_mountedMap$get = mountedMap.get(atom)) == null ? void 0 : _mountedMap$get.u; if (onUnmount) { onUnmount(); } mountedMap.delete(atom); if (process.env.NODE_ENV !== 'production') { mountedAtoms.delete(atom); } var atomState = getAtomState(atom); if (atomState) { if (hasPromiseAtomValue(atomState)) { cancelPromise(atomState.v); } atomState.d.forEach(function (_, a) { if (a !== atom) { var mounted = mountedMap.get(a); if (mounted) { mounted.t.delete(atom); if (canUnmountAtom(a, mounted)) { unmountAtom(a); } } } }); } else if (process.env.NODE_ENV !== 'production') { console.warn('[Bug] could not find atom state to unmount', atom); } }; var mountDependencies = function mountDependencies(atom, atomState, prevDependencies) { var depSet = new Set(atomState.d.keys()); prevDependencies == null ? void 0 : prevDependencies.forEach(function (_, a) { if (depSet.has(a)) { depSet.delete(a); return; } var mounted = mountedMap.get(a); if (mounted) { mounted.t.delete(atom); if (canUnmountAtom(a, mounted)) { unmountAtom(a); } } }); depSet.forEach(function (a) { var mounted = mountedMap.get(a); if (mounted) { mounted.t.add(atom); } else if (mountedMap.has(atom)) { mountAtom(a, atom); } }); }; var flushPending = function flushPending() { var flushed; if (process.env.NODE_ENV !== 'production') { flushed = new Set(); } while (pendingMap.size) { var pending = Array.from(pendingMap); pendingMap.clear(); pending.forEach(function (_ref2) { var atom = _ref2[0], prevAtomState = _ref2[1]; var atomState = getAtomState(atom); if (atomState) { if (atomState.d !== (prevAtomState == null ? void 0 : prevAtomState.d)) { mountDependencies(atom, atomState, prevAtomState == null ? void 0 : prevAtomState.d); } var mounted = mountedMap.get(atom); if (mounted && !(prevAtomState && !hasPromiseAtomValue(prevAtomState) && (isEqualAtomValue(prevAtomState, atomState) || isEqualAtomError(prevAtomState, atomState)))) { mounted.l.forEach(function (listener) { return listener(); }); if (process.env.NODE_ENV !== 'production') { flushed.add(atom); } } } else if (process.env.NODE_ENV !== 'production') { console.warn('[Bug] no atom state to flush'); } }); } if (process.env.NODE_ENV !== 'production') { storeListenersRev1.forEach(function (l) { return l('state'); }); return flushed; } }; var subscribeAtom = function subscribeAtom(atom, listener) { var mounted = addAtom(atom); var flushed = flushPending(); var listeners = mounted.l; listeners.add(listener); if (process.env.NODE_ENV !== 'production') { storeListenersRev1.forEach(function (l) { return l('sub'); }); storeListenersRev2.forEach(function (l) { return l({ type: 'sub', flushed: flushed }); }); } return function () { listeners.delete(listener); delAtom(atom); if (process.env.NODE_ENV !== 'production') { storeListenersRev1.forEach(function (l) { return l('unsub'); }); storeListenersRev2.forEach(function (l) { return l({ type: 'unsub' }); }); } }; }; if (process.env.NODE_ENV !== 'production') { return { get: readAtom, set: writeAtom, sub: subscribeAtom, dev_subscribe_store: function (l, rev) { if (rev !== 2) { console.warn('The current StoreListener revision is 2. The older ones are deprecated.'); storeListenersRev1.add(l); return function () { storeListenersRev1.delete(l); }; } storeListenersRev2.add(l); return function () { storeListenersRev2.delete(l); }; }, dev_get_mounted_atoms: function dev_get_mounted_atoms() { return mountedAtoms.values(); }, dev_get_atom_state: function dev_get_atom_state(a) { return atomStateMap.get(a); }, dev_get_mounted: function dev_get_mounted(a) { return mountedMap.get(a); }, dev_restore_atoms: function dev_restore_atoms(values) { for (var _iterator = _createForOfIteratorHelperLoose(values), _step; !(_step = _iterator()).done;) { var _step$value = _step.value, atom = _step$value[0], valueOrPromise = _step$value[1]; if (hasInitialValue(atom)) { setAtomValueOrPromise(atom, valueOrPromise); recomputeDependents(atom); } } var flushed = flushPending(); storeListenersRev2.forEach(function (l) { return l({ type: 'restore', flushed: flushed }); }); } }; } return { get: readAtom, set: writeAtom, sub: subscribeAtom }; }; var defaultStore; var getDefaultStore = function getDefaultStore() { if (!defaultStore) { defaultStore = createStore(); } return defaultStore; }; exports.atom = atom; exports.createStore = createStore; exports.getDefaultStore = getDefaultStore;