UNPKG

19.8 kBJavaScriptView Raw
1var jotai = (function (exports, react, useContextSelector) {
2 'use strict';
3
4 function _extends() {
5 _extends = Object.assign || function (target) {
6 for (var i = 1; i < arguments.length; i++) {
7 var source = arguments[i];
8
9 for (var key in source) {
10 if (Object.prototype.hasOwnProperty.call(source, key)) {
11 target[key] = source[key];
12 }
13 }
14 }
15
16 return target;
17 };
18
19 return _extends.apply(this, arguments);
20 }
21
22 var isSSR = typeof window === 'undefined' || /ServerSideRendering/.test(window.navigator && window.navigator.userAgent);
23 var useIsoLayoutEffect = isSSR ? function (fn) {
24 return fn();
25 } : react.useLayoutEffect;
26
27 var appendMap = function appendMap(dst, src) {
28 src.forEach(function (v, k) {
29 dst.set(k, v);
30 });
31 return dst;
32 }; // create new map from two maps
33
34
35 var concatMap = function concatMap(src1, src2) {
36 return appendMap(new Map(src1), src2);
37 }; // create new map and delete item
38
39
40 var deleteMapItem = function deleteMapItem(src, key) {
41 var dst = new Map(src);
42 dst.delete(key);
43 return dst;
44 };
45
46 var warningObject = new Proxy({}, {
47 get: function get() {
48 throw new Error('Please use <Provider>');
49 },
50 apply: function apply() {
51 throw new Error('Please use <Provider>');
52 }
53 });
54
55 var addDependent = function addDependent(dependentsMap, atom, dependent) {
56 var dependents = dependentsMap.get(atom);
57
58 if (!dependents) {
59 dependents = new Set();
60 dependentsMap.set(atom, dependents);
61 }
62
63 dependents.add(dependent);
64 };
65
66 var deleteDependent = function deleteDependent(dependentsMap, dependent) {
67 dependentsMap.forEach(function (dependents) {
68 dependents.delete(dependent);
69 });
70 };
71
72 var setDependencies = function setDependencies(dependentsMap, atom, dependencies) {
73 deleteDependent(dependentsMap, atom);
74 dependencies.forEach(function (dependency) {
75 addDependent(dependentsMap, dependency, atom);
76 });
77 };
78
79 var listDependents = function listDependents(dependentsMap, atom, excludeSelf) {
80 var dependents = new Set(dependentsMap.get(atom));
81
82 if (excludeSelf) {
83 dependents.delete(atom);
84 }
85
86 return dependents;
87 };
88
89 var initialState = new Map();
90
91 var getAtomState = function getAtomState(atom, state, tmpState) {
92 var atomState = tmpState && tmpState.get(atom) || state.get(atom);
93
94 if (!atomState) {
95 throw new Error('atom state not found. possibly a bug.');
96 }
97
98 return atomState;
99 };
100
101 var getAtomStateValue = function getAtomStateValue(atom, state, tmpState) {
102 var atomState = tmpState && tmpState.get(atom) || state.get(atom);
103 return atomState ? atomState.value : atom.init;
104 };
105
106 var readAtom = function readAtom(state, readingAtom, setState, dependentsMap, readPendingMap) {
107 var readAtomValue = function readAtomValue(prevState, atom) {
108 var partialState = new Map();
109 var atomState = prevState.get(atom);
110
111 if (atomState) {
112 return [atomState, partialState];
113 }
114
115 var error = undefined;
116 var promise = undefined;
117 var value = null;
118 var dependencies = new Set();
119 var isSync = true;
120
121 try {
122 var promiseOrValue = atom.read(function (a) {
123 if (dependencies) {
124 dependencies.add(a);
125 } else {
126 addDependent(dependentsMap, a, atom);
127 }
128
129 if (a !== atom) {
130 var _readAtomValue = readAtomValue(concatMap(prevState, partialState), a),
131 _nextAtomState = _readAtomValue[0],
132 nextPartialState = _readAtomValue[1];
133
134 if (isSync) {
135 appendMap(partialState, nextPartialState);
136 } else {
137 setState(function (prev) {
138 return concatMap(prev, nextPartialState);
139 });
140 }
141
142 if (_nextAtomState.readE) {
143 throw _nextAtomState.readE;
144 }
145
146 if (_nextAtomState.readP) {
147 throw _nextAtomState.readP;
148 }
149
150 return _nextAtomState.value;
151 } // a === atom
152
153
154 var aState = partialState.get(a) || prevState.get(a);
155
156 if (aState) {
157 if (aState.readP) {
158 throw aState.readP;
159 }
160
161 return aState.value;
162 }
163
164 return a.init; // this should not be undefined
165 });
166
167 if (promiseOrValue instanceof Promise) {
168 promise = promiseOrValue.then(function (value) {
169 var _prev$get;
170
171 setDependencies(dependentsMap, atom, dependencies);
172 dependencies = null;
173 var prev = readPendingMap.get(atom);
174
175 if (prev && ((_prev$get = prev.get(atom)) == null ? void 0 : _prev$get.readP) === promise) {
176 readPendingMap.set(atom, deleteMapItem(prev, atom));
177 }
178
179 setState(function (prev) {
180 return new Map(prev).set(atom, {
181 value: value
182 });
183 });
184 }).catch(function (e) {
185 var _prev$get2;
186
187 var prev = readPendingMap.get(atom);
188
189 if (prev && ((_prev$get2 = prev.get(atom)) == null ? void 0 : _prev$get2.readP) === promise) {
190 readPendingMap.set(atom, deleteMapItem(prev, atom));
191 }
192
193 setState(function (prev) {
194 return new Map(prev).set(atom, {
195 value: getAtomStateValue(atom, prev),
196 readE: e instanceof Error ? e : new Error(e)
197 });
198 });
199 });
200 } else {
201 setDependencies(dependentsMap, atom, dependencies);
202 dependencies = null;
203 value = promiseOrValue;
204 }
205 } catch (errorOrPromise) {
206 if (errorOrPromise instanceof Promise) {
207 promise = errorOrPromise;
208 } else if (errorOrPromise instanceof Error) {
209 error = errorOrPromise;
210 } else {
211 error = new Error(errorOrPromise);
212 }
213 }
214
215 var nextAtomState = {
216 readE: error,
217 readP: promise,
218 value: promise ? atom.init : value
219 };
220 partialState.set(atom, nextAtomState);
221 isSync = false;
222 return [nextAtomState, partialState];
223 };
224
225 var prevPartialState = readPendingMap.get(readingAtom);
226
227 var _readAtomValue2 = readAtomValue(prevPartialState ? concatMap(state, prevPartialState) : state, readingAtom),
228 atomState = _readAtomValue2[0],
229 partialState = _readAtomValue2[1];
230
231 readPendingMap.set(readingAtom, prevPartialState ? concatMap(prevPartialState, partialState) : partialState);
232 return atomState;
233 };
234
235 var addAtom = function addAtom(id, atom, setState, dependentsMap, readPendingMap) {
236 addDependent(dependentsMap, atom, id);
237 var partialState = readPendingMap.get(atom);
238
239 if (partialState) {
240 readPendingMap.delete(atom);
241 setState(function (prev) {
242 return concatMap(prev, partialState);
243 });
244 }
245 };
246
247 var delAtom = function delAtom(id, setGcCount, dependentsMap) {
248 deleteDependent(dependentsMap, id);
249 setGcCount(function (c) {
250 return c + 1;
251 }); // trigger re-render for gc
252 };
253
254 var gcAtom = function gcAtom(state, setState, dependentsMap) {
255 var nextState = new Map(state);
256 var deleted;
257
258 do {
259 deleted = false;
260 nextState.forEach(function (_atomState, atom) {
261 var _dependentsMap$get;
262
263 var isEmpty = ((_dependentsMap$get = dependentsMap.get(atom)) == null ? void 0 : _dependentsMap$get.size) === 0;
264
265 if (isEmpty) {
266 nextState.delete(atom);
267 dependentsMap.delete(atom);
268 deleted = true;
269 }
270 });
271 } while (deleted);
272
273 if (nextState.size !== state.size) {
274 setState(nextState);
275 }
276 };
277
278 var writeAtom = function writeAtom(updatingAtom, update, setState, dependentsMap, addWriteThunk) {
279 var pendingPromises = [];
280
281 var updateDependentsState = function updateDependentsState(prevState, atom) {
282 var partialState = new Map();
283 listDependents(dependentsMap, atom, true).forEach(function (dependent) {
284 if (typeof dependent === 'symbol') return;
285 var dependencies = new Set();
286
287 try {
288 var promiseOrValue = dependent.read(function (a) {
289 if (dependencies) {
290 dependencies.add(a);
291 } else {
292 addDependent(dependentsMap, a, dependent);
293 }
294
295 var s = getAtomState(a, prevState);
296
297 if (s.readE) {
298 throw s.readE;
299 }
300
301 return s.value;
302 });
303
304 if (promiseOrValue instanceof Promise) {
305 var promise = promiseOrValue.then(function (value) {
306 setDependencies(dependentsMap, dependent, dependencies);
307 dependencies = null;
308 var nextAtomState = {
309 value: value
310 };
311 setState(function (prev) {
312 var _prev$get3;
313
314 var prevPromise = (_prev$get3 = prev.get(dependent)) == null ? void 0 : _prev$get3.readP;
315
316 if (prevPromise && prevPromise !== promise) {
317 // There is a new promise, so we skip updating this one.
318 return prev;
319 }
320
321 var nextState = new Map(prev).set(dependent, nextAtomState);
322 var nextPartialState = updateDependentsState(nextState, dependent);
323 return appendMap(nextState, nextPartialState);
324 });
325 }).catch(function (e) {
326 setState(function (prev) {
327 return new Map(prev).set(dependent, {
328 value: getAtomStateValue(dependent, prev),
329 readE: e instanceof Error ? e : new Error(e)
330 });
331 });
332 });
333 partialState.set(dependent, {
334 value: getAtomStateValue(dependent, prevState),
335 readP: promise
336 });
337 } else {
338 setDependencies(dependentsMap, dependent, dependencies);
339 dependencies = null;
340 partialState.set(dependent, {
341 value: promiseOrValue
342 });
343 appendMap(partialState, updateDependentsState(concatMap(prevState, partialState), dependent));
344 }
345 } catch (e) {
346 partialState.set(dependent, {
347 value: getAtomStateValue(dependent, prevState),
348 readE: e instanceof Error ? e : new Error(e)
349 });
350 appendMap(partialState, updateDependentsState(concatMap(prevState, partialState), dependent));
351 }
352 });
353 return partialState;
354 };
355
356 var updateAtomState = function updateAtomState(prevState, atom, update) {
357 var prevAtomState = prevState.get(atom);
358
359 if (prevAtomState && prevAtomState.writeP) {
360 var promise = prevAtomState.writeP.then(function () {
361 addWriteThunk(function (prev) {
362 var nextPartialState = updateAtomState(prev, atom, update);
363
364 if (nextPartialState) {
365 return concatMap(prevState, nextPartialState);
366 }
367
368 return prev;
369 });
370 });
371 pendingPromises.push(promise);
372 return null;
373 }
374
375 var partialState = new Map();
376 var isSync = true;
377
378 try {
379 var promiseOrVoid = atom.write(function (a) {
380 if (process.env.NODE_ENV !== 'production') {
381 var s = prevState.get(a);
382
383 if (s && s.readP) {
384 console.log('Reading pending atom state in write operation. Not sure how to deal with it. Returning stale vaule for', a);
385 }
386 }
387
388 return getAtomStateValue(a, prevState, partialState);
389 }, function (a, v) {
390 if (a === atom) {
391 var nextAtomState = {
392 value: v
393 };
394
395 if (isSync) {
396 partialState.set(a, nextAtomState);
397 appendMap(partialState, updateDependentsState(concatMap(prevState, partialState), a));
398 } else {
399 setState(function (prev) {
400 var nextState = new Map(prev).set(a, nextAtomState);
401 var nextPartialState = updateDependentsState(nextState, a);
402 return appendMap(nextState, nextPartialState);
403 });
404 }
405 } else {
406 if (isSync) {
407 var nextPartialState = updateAtomState(concatMap(prevState, partialState), a, v);
408
409 if (nextPartialState) {
410 appendMap(partialState, nextPartialState);
411 }
412 } else {
413 addWriteThunk(function (prev) {
414 var nextPartialState = updateAtomState(prev, a, v);
415
416 if (nextPartialState) {
417 return concatMap(prev, nextPartialState);
418 }
419
420 return prev;
421 });
422 }
423 }
424 }, update);
425
426 if (promiseOrVoid instanceof Promise) {
427 pendingPromises.push(promiseOrVoid);
428
429 var nextAtomState = _extends({}, getAtomState(atom, prevState, partialState), {
430 writeP: promiseOrVoid.then(function () {
431 addWriteThunk(function (prev) {
432 return new Map(prev).set(atom, _extends({}, getAtomState(atom, prev), {
433 writeP: undefined
434 }));
435 });
436 })
437 });
438
439 partialState.set(atom, nextAtomState);
440 }
441 } catch (e) {
442 if (pendingPromises.length) {
443 pendingPromises.push(new Promise(function (_resolve, reject) {
444 reject(e);
445 }));
446 } else {
447 throw e;
448 }
449 }
450
451 isSync = false;
452 return partialState;
453 };
454
455 addWriteThunk(function (prevState) {
456 var nextPartialState = updateAtomState(prevState, updatingAtom, update);
457
458 if (nextPartialState) {
459 return concatMap(prevState, nextPartialState);
460 }
461
462 return prevState;
463 });
464
465 if (pendingPromises.length) {
466 return new Promise(function (resolve, reject) {
467 var loop = function loop() {
468 var len = pendingPromises.length;
469
470 if (len === 0) {
471 resolve();
472 } else {
473 Promise.all(pendingPromises).then(function () {
474 pendingPromises.splice(0, len);
475 loop();
476 }).catch(reject);
477 }
478 };
479
480 loop();
481 });
482 }
483 };
484
485 var runWriteThunk = function runWriteThunk(lastStateRef, setState, writeThunkQueue) {
486 while (true) {
487 if (!lastStateRef.current) {
488 return;
489 }
490
491 if (writeThunkQueue.length === 0) {
492 return;
493 }
494
495 var thunk = writeThunkQueue.shift();
496 var lastState = lastStateRef.current;
497 var nextState = thunk(lastState);
498
499 if (nextState !== lastState) {
500 setState(nextState);
501 return;
502 }
503 }
504 };
505
506 var ActionsContext = useContextSelector.createContext(warningObject);
507 var StateContext = useContextSelector.createContext(warningObject);
508 var Provider = function Provider(_ref) {
509 var children = _ref.children;
510 var dependentsMapRef = react.useRef();
511
512 if (!dependentsMapRef.current) {
513 dependentsMapRef.current = new Map();
514 }
515
516 var readPendingMapRef = react.useRef();
517
518 if (!readPendingMapRef.current) {
519 readPendingMapRef.current = new WeakMap();
520 }
521
522 var _useState = react.useState(initialState),
523 state = _useState[0],
524 setStateOrig = _useState[1];
525
526 var lastStateRef = react.useRef(null);
527
528 var setState = function setState(setStateAction) {
529 lastStateRef.current = null;
530 setStateOrig(setStateAction);
531 };
532
533 useIsoLayoutEffect(function () {
534 if (state !== initialState) {
535 lastStateRef.current = state;
536 }
537 });
538
539 var _useState2 = react.useState(0),
540 gcCount = _useState2[0],
541 setGcCount = _useState2[1]; // to trigger gc
542
543
544 react.useEffect(function () {
545 gcAtom(state, setState, dependentsMapRef.current);
546 }, [state, gcCount]);
547 var writeThunkQueueRef = react.useRef([]);
548 react.useEffect(function () {
549 runWriteThunk(lastStateRef, setState, writeThunkQueueRef.current);
550 }, [state]);
551 var actions = react.useMemo(function () {
552 return {
553 add: function add(id, atom) {
554 return addAtom(id, atom, setState, dependentsMapRef.current, readPendingMapRef.current);
555 },
556 del: function del(id) {
557 return delAtom(id, setGcCount, dependentsMapRef.current);
558 },
559 read: function read(state, atom) {
560 return readAtom(state, atom, setState, dependentsMapRef.current, readPendingMapRef.current);
561 },
562 write: function write(atom, update) {
563 return writeAtom(atom, update, setState, dependentsMapRef.current, function (thunk) {
564 writeThunkQueueRef.current.push(thunk);
565 runWriteThunk(lastStateRef, setState, writeThunkQueueRef.current);
566 });
567 }
568 };
569 }, []);
570 return react.createElement(ActionsContext.Provider, {
571 value: actions
572 }, react.createElement(StateContext.Provider, {
573 value: state
574 }, children));
575 };
576
577 var keyCount = 0; // global key count for all atoms
578
579 function atom(read, write) {
580 var config = {
581 key: ++keyCount
582 };
583
584 if (typeof read === 'function') {
585 config.read = read;
586 } else {
587 config.init = read;
588
589 config.read = function (get) {
590 return get(config);
591 };
592
593 config.write = function (get, set, update) {
594 set(config, typeof update === 'function' ? update(get(config)) : update);
595 };
596 }
597
598 if (write) {
599 config.write = write;
600 }
601
602 return config;
603 }
604
605 var isWritable = function isWritable(atom) {
606 return !!atom.write;
607 };
608
609 function useAtom(atom) {
610 var actions = useContextSelector.useContext(ActionsContext);
611 var value = useContextSelector.useContextSelector(StateContext, react.useCallback(function (state) {
612 var atomState = actions.read(state, atom);
613
614 if (atomState.readE) {
615 throw atomState.readE; // read error
616 }
617
618 if (atomState.readP) {
619 throw atomState.readP; // read promise
620 }
621
622 if (atomState.writeP) {
623 throw atomState.writeP; // write promise
624 }
625
626 return atomState.value;
627 }, [atom, actions]));
628 useIsoLayoutEffect(function () {
629 var id = Symbol();
630 actions.add(id, atom);
631 return function () {
632 actions.del(id);
633 };
634 }, [actions, atom]);
635 var setAtom = react.useCallback(function (update) {
636 if (isWritable(atom)) {
637 return actions.write(atom, update);
638 } else {
639 throw new Error('not writable atom');
640 }
641 }, [atom, actions]);
642 react.useDebugValue(value);
643 return [value, setAtom];
644 }
645
646 var useBridge = function useBridge() {
647 var actions = useContextSelector.useContext(ActionsContext);
648 var state = useContextSelector.useContext(StateContext);
649 return react.useMemo(function () {
650 return [actions, state];
651 }, [actions, state]);
652 };
653 var Bridge = function Bridge(_ref) {
654 var value = _ref.value,
655 children = _ref.children;
656 var actions = value[0],
657 state = value[1];
658 return react.createElement(useContextSelector.BridgeProvider, {
659 context: ActionsContext,
660 value: actions
661 }, react.createElement(useContextSelector.BridgeProvider, {
662 context: StateContext,
663 value: state
664 }, children));
665 };
666
667 exports.Bridge = Bridge;
668 exports.Provider = Provider;
669 exports.atom = atom;
670 exports.useAtom = useAtom;
671 exports.useBridge = useBridge;
672
673 return exports;
674
675}({}, React, useContextSelector));