UNPKG

12.6 kBJavaScriptView Raw
1import { useContext, useCallback, useMemo } from 'react';
2import { SECRET_INTERNAL_getStoreContext, useAtom, atom } from 'jotai';
3
4function useUpdateAtom(anAtom) {
5 const StoreContext = SECRET_INTERNAL_getStoreContext(anAtom.scope);
6 const [, updateAtom] = useContext(StoreContext);
7 const setAtom = useCallback((update) => updateAtom(anAtom, update), [updateAtom, anAtom]);
8 return setAtom;
9}
10
11function useAtomValue(anAtom) {
12 return useAtom(anAtom)[0];
13}
14
15const RESET = Symbol();
16function atomWithReset(initialValue) {
17 const anAtom = atom(initialValue, (get, set, update) => {
18 if (update === RESET) {
19 set(anAtom, initialValue);
20 } else {
21 set(anAtom, typeof update === "function" ? update(get(anAtom)) : update);
22 }
23 });
24 return anAtom;
25}
26
27function useResetAtom(anAtom) {
28 const StoreContext = SECRET_INTERNAL_getStoreContext(anAtom.scope);
29 const [, updateAtom] = useContext(StoreContext);
30 const setAtom = useCallback(() => updateAtom(anAtom, RESET), [updateAtom, anAtom]);
31 return setAtom;
32}
33
34function useReducerAtom(anAtom, reducer) {
35 const [state, setState] = useAtom(anAtom);
36 const dispatch = useCallback((action) => {
37 setState((prev) => reducer(prev, action));
38 }, [setState, reducer]);
39 return [state, dispatch];
40}
41
42function atomWithReducer(initialValue, reducer) {
43 const anAtom = atom(initialValue, (get, set, action) => set(anAtom, reducer(get(anAtom), action)));
44 return anAtom;
45}
46
47function atomFamily(initializeAtom, areEqual) {
48 let shouldRemove = null;
49 const atoms = new Map();
50 const createAtom = (param) => {
51 let item;
52 if (areEqual === void 0) {
53 item = atoms.get(param);
54 } else {
55 for (let [key, value] of atoms) {
56 if (areEqual(key, param)) {
57 item = value;
58 break;
59 }
60 }
61 }
62 if (item !== void 0) {
63 if (shouldRemove == null ? void 0 : shouldRemove(item[1], param)) {
64 atoms.delete(param);
65 } else {
66 return item[0];
67 }
68 }
69 const newAtom = initializeAtom(param);
70 atoms.set(param, [newAtom, Date.now()]);
71 return newAtom;
72 };
73 createAtom.remove = (param) => {
74 if (areEqual === void 0) {
75 atoms.delete(param);
76 } else {
77 for (let [key] of atoms) {
78 if (areEqual(key, param)) {
79 atoms.delete(key);
80 break;
81 }
82 }
83 }
84 };
85 createAtom.setShouldRemove = (fn) => {
86 shouldRemove = fn;
87 if (!shouldRemove)
88 return;
89 for (let [key, value] of atoms) {
90 if (shouldRemove(value[1], key)) {
91 atoms.delete(key);
92 }
93 }
94 };
95 return createAtom;
96}
97
98const getWeakCacheItem = (cache, deps) => {
99 const [dep, ...rest] = deps;
100 const entry = cache.get(dep);
101 if (!entry) {
102 return;
103 }
104 if (!rest.length) {
105 return entry[1];
106 }
107 return getWeakCacheItem(entry[0], rest);
108};
109const setWeakCacheItem = (cache, deps, item) => {
110 const [dep, ...rest] = deps;
111 let entry = cache.get(dep);
112 if (!entry) {
113 entry = [new WeakMap()];
114 cache.set(dep, entry);
115 }
116 if (!rest.length) {
117 entry[1] = item;
118 return;
119 }
120 setWeakCacheItem(entry[0], rest, item);
121};
122
123const selectAtomCache = new WeakMap();
124function selectAtom(anAtom, selector, equalityFn = Object.is) {
125 const deps = [anAtom, selector, equalityFn];
126 const cachedAtom = getWeakCacheItem(selectAtomCache, deps);
127 if (cachedAtom) {
128 return cachedAtom;
129 }
130 let initialized = false;
131 let prevSlice;
132 const derivedAtom = atom((get) => {
133 const slice = selector(get(anAtom));
134 if (initialized && equalityFn(prevSlice, slice)) {
135 return prevSlice;
136 }
137 initialized = true;
138 prevSlice = slice;
139 return slice;
140 });
141 derivedAtom.scope = anAtom.scope;
142 setWeakCacheItem(selectAtomCache, deps, derivedAtom);
143 return derivedAtom;
144}
145
146function useAtomCallback(callback, scope) {
147 const anAtom = useMemo(() => atom(null, (get, set, [arg, resolve, reject]) => {
148 try {
149 resolve(callback(get, set, arg));
150 } catch (e) {
151 reject(e);
152 }
153 }), [callback]);
154 anAtom.scope = scope;
155 const [, invoke] = useAtom(anAtom);
156 return useCallback((arg) => new Promise((resolve, reject) => {
157 invoke([arg, resolve, reject]);
158 }), [invoke]);
159}
160
161const freezeAtomCache = new WeakMap();
162const deepFreeze = (obj) => {
163 if (typeof obj !== "object" || obj === null)
164 return;
165 Object.freeze(obj);
166 const propNames = Object.getOwnPropertyNames(obj);
167 for (const name of propNames) {
168 const value = obj[name];
169 deepFreeze(value);
170 }
171 return obj;
172};
173function freezeAtom(anAtom) {
174 const deps = [anAtom];
175 const cachedAtom = getWeakCacheItem(freezeAtomCache, deps);
176 if (cachedAtom) {
177 return cachedAtom;
178 }
179 const frozenAtom = atom((get) => deepFreeze(get(anAtom)), (_get, set, arg) => set(anAtom, arg));
180 frozenAtom.scope = anAtom.scope;
181 setWeakCacheItem(freezeAtomCache, deps, frozenAtom);
182 return frozenAtom;
183}
184function freezeAtomCreator(createAtom) {
185 return (...params) => {
186 const anAtom = createAtom(...params);
187 const origRead = anAtom.read;
188 anAtom.read = (get) => deepFreeze(origRead(get));
189 return anAtom;
190 };
191}
192
193const splitAtomCache = new WeakMap();
194const isWritable = (atom2) => !!atom2.write;
195const isFunction = (x) => typeof x === "function";
196function splitAtom(arrAtom, keyExtractor) {
197 const deps = keyExtractor ? [arrAtom, keyExtractor] : [arrAtom];
198 const cachedAtom = getWeakCacheItem(splitAtomCache, deps);
199 if (cachedAtom) {
200 return cachedAtom;
201 }
202 let currentAtomList;
203 let currentKeyList;
204 const keyToAtom = (key) => {
205 const index = currentKeyList == null ? void 0 : currentKeyList.indexOf(key);
206 if (index === void 0 || index === -1) {
207 return void 0;
208 }
209 return currentAtomList == null ? void 0 : currentAtomList[index];
210 };
211 const read = (get) => {
212 let nextAtomList = [];
213 let nextKeyList = [];
214 get(arrAtom).forEach((item, index) => {
215 const key = keyExtractor ? keyExtractor(item) : index;
216 nextKeyList[index] = key;
217 const cachedAtom2 = keyToAtom(key);
218 if (cachedAtom2) {
219 nextAtomList[index] = cachedAtom2;
220 return;
221 }
222 const read2 = (get2) => {
223 const index2 = currentKeyList == null ? void 0 : currentKeyList.indexOf(key);
224 if (index2 === void 0 || index2 === -1) {
225 throw new Error("index not found");
226 }
227 return get2(arrAtom)[index2];
228 };
229 const write2 = (get2, set, update) => {
230 const index2 = currentKeyList == null ? void 0 : currentKeyList.indexOf(key);
231 if (index2 === void 0 || index2 === -1) {
232 throw new Error("index not found");
233 }
234 const prev = get2(arrAtom);
235 const nextItem = isFunction(update) ? update(prev[index2]) : update;
236 set(arrAtom, [
237 ...prev.slice(0, index2),
238 nextItem,
239 ...prev.slice(index2 + 1)
240 ]);
241 };
242 const itemAtom = isWritable(arrAtom) ? atom(read2, write2) : atom(read2);
243 itemAtom.scope = arrAtom.scope;
244 nextAtomList[index] = itemAtom;
245 });
246 currentKeyList = nextKeyList;
247 if (currentAtomList && currentAtomList.length === nextAtomList.length && currentAtomList.every((x, i) => x === nextAtomList[i])) {
248 return currentAtomList;
249 }
250 return currentAtomList = nextAtomList;
251 };
252 const write = (get, set, atomToRemove) => {
253 const index = get(splittedAtom).indexOf(atomToRemove);
254 if (index >= 0) {
255 const prev = get(arrAtom);
256 set(arrAtom, [
257 ...prev.slice(0, index),
258 ...prev.slice(index + 1)
259 ]);
260 }
261 };
262 const splittedAtom = isWritable(arrAtom) ? atom(read, write) : atom(read);
263 splittedAtom.scope = arrAtom.scope;
264 setWeakCacheItem(splitAtomCache, deps, splittedAtom);
265 return splittedAtom;
266}
267
268function atomWithDefault(getDefault) {
269 const EMPTY = Symbol();
270 const overwrittenAtom = atom(EMPTY);
271 const anAtom = atom((get) => {
272 const overwritten = get(overwrittenAtom);
273 if (overwritten !== EMPTY) {
274 return overwritten;
275 }
276 return getDefault(get);
277 }, (get, set, update) => set(overwrittenAtom, typeof update === "function" ? update(get(anAtom)) : update));
278 return anAtom;
279}
280
281var __defProp = Object.defineProperty;
282var __defProps = Object.defineProperties;
283var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
284var __getOwnPropSymbols = Object.getOwnPropertySymbols;
285var __hasOwnProp = Object.prototype.hasOwnProperty;
286var __propIsEnum = Object.prototype.propertyIsEnumerable;
287var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, {enumerable: true, configurable: true, writable: true, value}) : obj[key] = value;
288var __spreadValues = (a, b) => {
289 for (var prop in b || (b = {}))
290 if (__hasOwnProp.call(b, prop))
291 __defNormalProp(a, prop, b[prop]);
292 if (__getOwnPropSymbols)
293 for (var prop of __getOwnPropSymbols(b)) {
294 if (__propIsEnum.call(b, prop))
295 __defNormalProp(a, prop, b[prop]);
296 }
297 return a;
298};
299var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
300const waitForAllCache = new WeakMap();
301function waitForAll(atoms) {
302 const cachedAtom = Array.isArray(atoms) && getWeakCacheItem(waitForAllCache, atoms);
303 if (cachedAtom) {
304 return cachedAtom;
305 }
306 const unwrappedAtoms = unwrapAtoms(atoms);
307 const derivedAtom = atom((get) => {
308 const promises = [];
309 const values = unwrappedAtoms.map((anAtom, index) => {
310 try {
311 return get(anAtom);
312 } catch (e) {
313 if (e instanceof Promise) {
314 promises[index] = e;
315 } else {
316 throw e;
317 }
318 }
319 });
320 if (promises.length) {
321 throw Promise.all(promises);
322 }
323 return wrapResults(atoms, values);
324 });
325 const waitForAllScope = unwrappedAtoms[0].scope;
326 derivedAtom.scope = waitForAllScope;
327 validateAtomScopes(waitForAllScope, unwrappedAtoms);
328 if (Array.isArray(atoms)) {
329 setWeakCacheItem(waitForAllCache, atoms, derivedAtom);
330 }
331 return derivedAtom;
332}
333const unwrapAtoms = (atoms) => Array.isArray(atoms) ? atoms : Object.getOwnPropertyNames(atoms).map((key) => atoms[key]);
334const wrapResults = (atoms, results) => Array.isArray(atoms) ? results : Object.getOwnPropertyNames(atoms).reduce((out, key, idx) => __spreadProps(__spreadValues({}, out), {[key]: results[idx]}), {});
335function validateAtomScopes(scope, atoms) {
336 if (scope && !atoms.every((a) => a.scope === scope)) {
337 console.warn("Different scopes were found for atoms supplied to waitForAll. This is unsupported and will result in unexpected behavior.");
338 }
339}
340
341function atomWithHash(key, initialValue, serialize = JSON.stringify, deserialize = JSON.parse) {
342 const anAtom = atom(initialValue, (get, set, update) => {
343 const newValue = typeof update === "function" ? update(get(anAtom)) : update;
344 set(anAtom, newValue);
345 const searchParams = new URLSearchParams(location.hash.slice(1));
346 searchParams.set(key, serialize(newValue));
347 location.hash = searchParams.toString();
348 });
349 anAtom.onMount = (setAtom) => {
350 const callback = () => {
351 const searchParams = new URLSearchParams(location.hash.slice(1));
352 const str = searchParams.get(key);
353 if (str !== null) {
354 setAtom(deserialize(str));
355 }
356 };
357 window.addEventListener("hashchange", callback);
358 callback();
359 return () => {
360 window.removeEventListener("hashchange", callback);
361 };
362 };
363 return anAtom;
364}
365
366const defaultStorage = {
367 getItem: (key) => {
368 const storedValue = localStorage.getItem(key);
369 if (storedValue === null) {
370 throw new Error("no value stored");
371 }
372 return JSON.parse(storedValue);
373 },
374 setItem: (key, newValue) => {
375 localStorage.setItem(key, JSON.stringify(newValue));
376 }
377};
378function atomWithStorage(key, initialValue, storage = defaultStorage) {
379 const getInitialValue = () => {
380 try {
381 return storage.getItem(key);
382 } catch {
383 return initialValue;
384 }
385 };
386 const baseAtom = atom(initialValue);
387 baseAtom.onMount = (setAtom) => {
388 const value = getInitialValue();
389 if (value instanceof Promise) {
390 value.then(setAtom);
391 } else {
392 setAtom(value);
393 }
394 };
395 const anAtom = atom((get) => get(baseAtom), (get, set, update) => {
396 const newValue = typeof update === "function" ? update(get(baseAtom)) : update;
397 set(baseAtom, newValue);
398 storage.setItem(key, newValue);
399 });
400 return anAtom;
401}
402
403export { RESET, atomFamily, atomWithDefault, atomWithHash, atomWithReducer, atomWithReset, atomWithStorage, freezeAtom, freezeAtomCreator, selectAtom, splitAtom, useAtomCallback, useAtomValue, useReducerAtom, useResetAtom, useUpdateAtom, waitForAll };