jotai
Version:
👻 Next gen state management that will spook you
559 lines (537 loc) • 17.1 kB
JavaScript
import { useContext, useCallback, useMemo } from 'react';
import { SECRET_INTERNAL_getScopeContext, useAtom, atom } from 'jotai';
const RESET = Symbol();
typeof require !== "undefined" ? require : (x) => {
throw new Error('Dynamic require of "' + x + '" is not supported');
};
const WRITE_ATOM = "w";
const RESTORE_ATOMS = "h";
function useUpdateAtom(anAtom, scope) {
const ScopeContext = SECRET_INTERNAL_getScopeContext(scope);
const store = useContext(ScopeContext)[0];
const setAtom = useCallback((update) => store[WRITE_ATOM](anAtom, update), [store, anAtom]);
return setAtom;
}
function useAtomValue(anAtom, scope) {
return useAtom(anAtom, scope)[0];
}
function atomWithReset(initialValue) {
const anAtom = atom(initialValue, (get, set, update) => {
if (update === RESET) {
set(anAtom, initialValue);
} else {
set(anAtom, typeof update === "function" ? update(get(anAtom)) : update);
}
});
return anAtom;
}
function useResetAtom(anAtom, scope) {
const ScopeContext = SECRET_INTERNAL_getScopeContext(scope);
const store = useContext(ScopeContext)[0];
const setAtom = useCallback(() => store[WRITE_ATOM](anAtom, RESET), [store, anAtom]);
return setAtom;
}
function useReducerAtom(anAtom, reducer, scope) {
const [state, setState] = useAtom(anAtom, scope);
const dispatch = useCallback((action) => {
setState((prev) => reducer(prev, action));
}, [setState, reducer]);
return [state, dispatch];
}
function atomWithReducer(initialValue, reducer) {
const anAtom = atom(initialValue, (get, set, action) => set(anAtom, reducer(get(anAtom), action)));
return anAtom;
}
function atomFamily(initializeAtom, areEqual) {
let shouldRemove = null;
const atoms = new Map();
const createAtom = (param) => {
let item;
if (areEqual === void 0) {
item = atoms.get(param);
} else {
for (let [key, value] of atoms) {
if (areEqual(key, param)) {
item = value;
break;
}
}
}
if (item !== void 0) {
if (shouldRemove == null ? void 0 : shouldRemove(item[1], param)) {
atoms.delete(param);
} else {
return item[0];
}
}
const newAtom = initializeAtom(param);
atoms.set(param, [newAtom, Date.now()]);
return newAtom;
};
createAtom.remove = (param) => {
if (areEqual === void 0) {
atoms.delete(param);
} else {
for (let [key] of atoms) {
if (areEqual(key, param)) {
atoms.delete(key);
break;
}
}
}
};
createAtom.setShouldRemove = (fn) => {
shouldRemove = fn;
if (!shouldRemove)
return;
for (let [key, value] of atoms) {
if (shouldRemove(value[1], key)) {
atoms.delete(key);
}
}
};
return createAtom;
}
const getWeakCacheItem = (cache, deps) => {
while (true) {
const [dep, ...rest] = deps;
const entry = cache.get(dep);
if (!entry) {
return;
}
if (!rest.length) {
return entry[1];
}
cache = entry[0];
deps = rest;
}
};
const setWeakCacheItem = (cache, deps, item) => {
while (true) {
const [dep, ...rest] = deps;
let entry = cache.get(dep);
if (!entry) {
entry = [new WeakMap()];
cache.set(dep, entry);
}
if (!rest.length) {
entry[1] = item;
return;
}
cache = entry[0];
deps = rest;
}
};
const createMemoizeAtom = () => {
const cache = new WeakMap();
const memoizeAtom = (createAtom, deps) => {
const cachedAtom = getWeakCacheItem(cache, deps);
if (cachedAtom) {
return cachedAtom;
}
const createdAtom = createAtom();
setWeakCacheItem(cache, deps, createdAtom);
return createdAtom;
};
return memoizeAtom;
};
const memoizeAtom$4 = createMemoizeAtom();
function selectAtom(anAtom, selector, equalityFn = Object.is) {
return memoizeAtom$4(() => {
const refAtom = atom(() => ({}));
const derivedAtom = atom((get) => {
const slice = selector(get(anAtom));
const ref = get(refAtom);
if ("prev" in ref && equalityFn(ref.prev, slice)) {
return ref.prev;
}
ref.prev = slice;
return slice;
});
return derivedAtom;
}, [anAtom, selector, equalityFn]);
}
function useAtomCallback(callback, scope) {
const anAtom = useMemo(() => atom(null, (get, set, [arg, resolve, reject]) => {
try {
resolve(callback(get, set, arg));
} catch (e) {
reject(e);
}
}), [callback]);
const invoke = useUpdateAtom(anAtom, scope);
return useCallback((arg) => new Promise((resolve, reject) => {
invoke([arg, resolve, reject]);
}), [invoke]);
}
const memoizeAtom$3 = createMemoizeAtom();
const deepFreeze = (obj) => {
if (typeof obj !== "object" || obj === null)
return;
Object.freeze(obj);
const propNames = Object.getOwnPropertyNames(obj);
for (const name of propNames) {
const value = obj[name];
deepFreeze(value);
}
return obj;
};
function freezeAtom(anAtom) {
return memoizeAtom$3(() => {
const frozenAtom = atom((get) => deepFreeze(get(anAtom)), (_get, set, arg) => set(anAtom, arg));
return frozenAtom;
}, [anAtom]);
}
function freezeAtomCreator(createAtom) {
return (...params) => {
const anAtom = createAtom(...params);
const origRead = anAtom.read;
anAtom.read = (get) => deepFreeze(origRead(get));
return anAtom;
};
}
const memoizeAtom$2 = createMemoizeAtom();
const isWritable = (atom2) => !!atom2.write;
const isFunction = (x) => typeof x === "function";
function splitAtom(arrAtom, keyExtractor) {
return memoizeAtom$2(() => {
const refAtom = atom(() => ({}));
const read = (get) => {
const ref = get(refAtom);
let nextAtomList = [];
let nextKeyList = [];
get(arrAtom).forEach((item, index) => {
var _a, _b, _c;
const key = keyExtractor ? keyExtractor(item) : index;
nextKeyList[index] = key;
const cachedAtom = (_c = ref.atomList) == null ? void 0 : _c[(_b = (_a = ref.keyList) == null ? void 0 : _a.indexOf(key)) != null ? _b : -1];
if (cachedAtom) {
nextAtomList[index] = cachedAtom;
return;
}
const read2 = (get2) => {
var _a2, _b2;
const index2 = (_b2 = (_a2 = ref.keyList) == null ? void 0 : _a2.indexOf(key)) != null ? _b2 : -1;
if (index2 === -1 && typeof process === "object" && process.env.NODE_ENV !== "production") {
console.warn("splitAtom: array index out of bounds, returning undefined", atom);
}
return get2(arrAtom)[index2];
};
const write2 = (get2, set, update) => {
var _a2, _b2;
const index2 = (_b2 = (_a2 = ref.keyList) == null ? void 0 : _a2.indexOf(key)) != null ? _b2 : -1;
if (index2 === -1) {
throw new Error("splitAtom: array index not found");
}
const prev = get2(arrAtom);
const nextItem = isFunction(update) ? update(prev[index2]) : update;
set(arrAtom, [
...prev.slice(0, index2),
nextItem,
...prev.slice(index2 + 1)
]);
};
const itemAtom = isWritable(arrAtom) ? atom(read2, write2) : atom(read2);
nextAtomList[index] = itemAtom;
});
ref.keyList = nextKeyList;
if (ref.atomList && ref.atomList.length === nextAtomList.length && ref.atomList.every((x, i) => x === nextAtomList[i])) {
return ref.atomList;
}
return ref.atomList = nextAtomList;
};
const write = (get, set, atomToRemove) => {
const index = get(splittedAtom).indexOf(atomToRemove);
if (index >= 0) {
const prev = get(arrAtom);
set(arrAtom, [
...prev.slice(0, index),
...prev.slice(index + 1)
]);
}
};
const splittedAtom = isWritable(arrAtom) ? atom(read, write) : atom(read);
return splittedAtom;
}, keyExtractor ? [arrAtom, keyExtractor] : [arrAtom]);
}
function atomWithDefault(getDefault) {
const EMPTY = Symbol();
const overwrittenAtom = atom(EMPTY);
const anAtom = atom((get) => {
const overwritten = get(overwrittenAtom);
if (overwritten !== EMPTY) {
return overwritten;
}
return getDefault(get);
}, (get, set, update) => {
if (update === RESET) {
set(overwrittenAtom, EMPTY);
} else {
set(overwrittenAtom, typeof update === "function" ? update(get(anAtom)) : update);
}
});
return anAtom;
}
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
typeof require !== "undefined" ? require : (x) => {
throw new Error('Dynamic require of "' + x + '" is not supported');
};
const memoizeAtom$1 = createMemoizeAtom();
function waitForAll(atoms) {
const createAtom = () => {
const unwrappedAtoms = unwrapAtoms(atoms);
const derivedAtom = atom((get) => {
const promises = [];
const values = unwrappedAtoms.map((anAtom, index) => {
try {
return get(anAtom);
} catch (e) {
if (e instanceof Promise) {
promises[index] = e;
} else {
throw e;
}
}
});
if (promises.length) {
throw Promise.all(promises);
}
return wrapResults(atoms, values);
});
return derivedAtom;
};
if (Array.isArray(atoms)) {
return memoizeAtom$1(createAtom, atoms);
}
return createAtom();
}
const unwrapAtoms = (atoms) => Array.isArray(atoms) ? atoms : Object.getOwnPropertyNames(atoms).map((key) => atoms[key]);
const wrapResults = (atoms, results) => Array.isArray(atoms) ? results : Object.getOwnPropertyNames(atoms).reduce((out, key, idx) => __spreadProps(__spreadValues({}, out), { [key]: results[idx] }), {});
const createJSONStorage = (getStringStorage) => ({
getItem: (key) => {
const value = getStringStorage().getItem(key);
if (value instanceof Promise) {
return value.then((v) => JSON.parse(v || ""));
}
return JSON.parse(value || "");
},
setItem: (key, newValue) => {
getStringStorage().setItem(key, JSON.stringify(newValue));
}
});
const defaultStorage = createJSONStorage(() => localStorage);
function atomWithStorage(key, initialValue, storage = defaultStorage) {
const getInitialValue = () => {
try {
const value = storage.getItem(key);
if (value instanceof Promise) {
return value.catch(() => initialValue);
}
return value;
} catch {
return initialValue;
}
};
const baseAtom = atom(storage.delayInit ? initialValue : getInitialValue());
baseAtom.onMount = (setAtom) => {
let unsub;
if (storage.subscribe) {
unsub = storage.subscribe(key, setAtom);
}
if (storage.delayInit) {
const value = getInitialValue();
if (value instanceof Promise) {
value.then(setAtom);
} else {
setAtom(value);
}
}
return unsub;
};
const anAtom = atom((get) => get(baseAtom), (get, set, update) => {
const newValue = typeof update === "function" ? update(get(baseAtom)) : update;
set(baseAtom, newValue);
storage.setItem(key, newValue);
});
return anAtom;
}
function atomWithHash(key, initialValue, serialize = JSON.stringify, deserialize = JSON.parse) {
const hashStorage = {
getItem: (key2) => {
const searchParams = new URLSearchParams(location.hash.slice(1));
const storedValue = searchParams.get(key2);
if (storedValue === null) {
throw new Error("no value stored");
}
return deserialize(storedValue);
},
setItem: (key2, newValue) => {
const searchParams = new URLSearchParams(location.hash.slice(1));
searchParams.set(key2, serialize(newValue));
location.hash = searchParams.toString();
},
delayInit: true,
subscribe: (key2, setValue) => {
const callback = () => {
const searchParams = new URLSearchParams(location.hash.slice(1));
const str = searchParams.get(key2);
if (str !== null) {
setValue(deserialize(str));
}
};
window.addEventListener("hashchange", callback);
return () => {
window.removeEventListener("hashchange", callback);
};
}
};
return atomWithStorage(key, initialValue, hashStorage);
}
function atomWithObservable(createObservable) {
const observableResultAtom = atom((get) => {
let settlePromise = null;
let observable = createObservable(get);
const returnsItself = observable[Symbol.observable];
if (returnsItself) {
observable = returnsItself();
}
const dataAtom = atom(new Promise((resolve, reject) => {
settlePromise = (data, err) => {
if (err) {
reject(err);
} else {
resolve(data);
}
};
}));
let setData = () => {
throw new Error("setting data without mount");
};
const dataListener = (data) => {
if (settlePromise) {
settlePromise(data);
settlePromise = null;
if (subscription && !setData) {
subscription.unsubscribe();
subscription = null;
}
} else {
setData(data);
}
};
const errorListener = (error) => {
if (settlePromise) {
settlePromise(null, error);
settlePromise = null;
if (subscription && !setData) {
subscription.unsubscribe();
subscription = null;
}
} else {
setData(Promise.reject(error));
}
};
let subscription = null;
subscription = observable.subscribe(dataListener, errorListener);
if (!settlePromise) {
subscription.unsubscribe();
subscription = null;
}
dataAtom.onMount = (update) => {
setData = update;
if (!subscription) {
subscription = observable.subscribe(dataListener, errorListener);
}
return () => subscription == null ? void 0 : subscription.unsubscribe();
};
return { dataAtom, observable };
});
const observableAtom = atom((get) => {
const { dataAtom } = get(observableResultAtom);
return get(dataAtom);
}, (get, _set, data) => {
const { observable } = get(observableResultAtom);
if ("next" in observable) {
observable.next(data);
} else {
throw new Error("observable is not subject");
}
});
return observableAtom;
}
const hydratedMap = new WeakMap();
function useHydrateAtoms(values, scope) {
const ScopeContext = SECRET_INTERNAL_getScopeContext(scope);
const scopeContainer = useContext(ScopeContext);
const store = scopeContainer[0];
const hydratedSet = getHydratedSet(scopeContainer);
const tuplesToRestore = [];
for (const tuple of values) {
const atom = tuple[0];
if (!hydratedSet.has(atom)) {
hydratedSet.add(atom);
tuplesToRestore.push(tuple);
}
}
if (tuplesToRestore.length) {
store[RESTORE_ATOMS](tuplesToRestore);
}
}
function getHydratedSet(scopeContainer) {
let hydratedSet = hydratedMap.get(scopeContainer);
if (!hydratedSet) {
hydratedSet = new WeakSet();
hydratedMap.set(scopeContainer, hydratedSet);
}
return hydratedSet;
}
const memoizeAtom = createMemoizeAtom();
const errorLoadableCache = new WeakMap();
const LOADING_LOADABLE = { state: "loading" };
function loadable(anAtom) {
return memoizeAtom(() => {
const derivedAtom = atom((get) => {
try {
const value = get(anAtom);
return {
state: "hasData",
data: value
};
} catch (error) {
if (error instanceof Promise) {
return LOADING_LOADABLE;
}
const cachedErrorLoadable = errorLoadableCache.get(error);
if (cachedErrorLoadable) {
return cachedErrorLoadable;
}
const errorLoadable = {
state: "hasError",
error
};
errorLoadableCache.set(error, errorLoadable);
return errorLoadable;
}
});
return derivedAtom;
}, [anAtom]);
}
export { RESET, atomFamily, atomWithDefault, atomWithHash, atomWithObservable, atomWithReducer, atomWithReset, atomWithStorage, createJSONStorage, freezeAtom, freezeAtomCreator, loadable, selectAtom, splitAtom, useAtomCallback, useAtomValue, useHydrateAtoms, useReducerAtom, useResetAtom, useUpdateAtom, waitForAll };