UNPKG

16.5 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 var dst = new Map();
37 src1.forEach(function (v, k) {
38 dst.set(k, v);
39 });
40 src2.forEach(function (v, k) {
41 dst.set(k, v);
42 });
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, atom, dependent) {
67 var dependents = dependentsMap.get(atom);
68
69 if (dependents && dependents.has(dependent)) {
70 dependents.delete(dependent);
71 return dependents.size === 0; // empty
72 }
73
74 return false; // not found
75 };
76
77 var listDependents = function listDependents(dependentsMap, atom) {
78 var dependents = dependentsMap.get(atom);
79 return dependents || new Set();
80 };
81
82 var initialState = new Map();
83
84 var getAtomStateValue = function getAtomStateValue(state, atom) {
85 var atomState = state.get(atom);
86 return atomState ? atomState.value : atom.init;
87 };
88
89 var readAtom = function readAtom(state, readingAtom, setState, dependentsMap) {
90 var readAtomValue = function readAtomValue(prevState, atom, dependent) {
91 if (dependent) {
92 addDependent(dependentsMap, atom, dependent);
93 }
94
95 var partialState = new Map();
96 var atomState = prevState.get(atom);
97
98 if (atomState) {
99 return [atomState, partialState];
100 }
101
102 var error = undefined;
103 var promise = undefined;
104 var value = null;
105 var isSync = true;
106
107 try {
108 var promiseOrValue = atom.read(function (a) {
109 if (a !== atom) {
110 var _readAtomValue = readAtomValue(prevState, a, atom),
111 _nextAtomState = _readAtomValue[0],
112 nextPartialState = _readAtomValue[1];
113
114 if (isSync) {
115 appendMap(partialState, nextPartialState);
116 } else {
117 setState(function (prev) {
118 return appendMap(new Map(prev), nextPartialState);
119 });
120 }
121
122 if (_nextAtomState.error) {
123 throw _nextAtomState.error;
124 }
125
126 if (_nextAtomState.promise) {
127 throw _nextAtomState.promise;
128 }
129
130 return _nextAtomState.value;
131 } // primitive atom
132
133
134 var aState = prevState.get(a);
135
136 if (aState) {
137 if (aState.promise) {
138 throw aState.promise;
139 }
140
141 return aState.value;
142 }
143
144 return a.init; // this should not be undefined
145 });
146
147 if (promiseOrValue instanceof Promise) {
148 promise = promiseOrValue.then(function (value) {
149 setState(function (prev) {
150 return new Map(prev).set(atom, {
151 value: value
152 });
153 });
154 }).catch(function (e) {
155 setState(function (prev) {
156 return new Map(prev).set(atom, {
157 value: getAtomStateValue(prev, atom),
158 error: e instanceof Error ? e : new Error(e)
159 });
160 });
161 });
162 } else {
163 value = promiseOrValue;
164 }
165 } catch (errorOrPromise) {
166 if (errorOrPromise instanceof Promise) {
167 promise = errorOrPromise;
168 } else if (errorOrPromise instanceof Error) {
169 error = errorOrPromise;
170 } else {
171 error = new Error(errorOrPromise);
172 }
173 }
174
175 var nextAtomState = {
176 error: error,
177 promise: promise,
178 value: promise ? atom.init : value
179 };
180 partialState.set(atom, nextAtomState);
181 isSync = false;
182 return [nextAtomState, partialState];
183 };
184
185 return readAtomValue(state, readingAtom, null);
186 };
187
188 var addAtom = function addAtom(id, atom, partialState, setState, dependentsMap) {
189 addDependent(dependentsMap, atom, id);
190
191 if (partialState) {
192 setState(function (prev) {
193 return appendMap(new Map(prev), partialState);
194 });
195 }
196 };
197
198 var delAtom = function delAtom(id, setState, dependentsMap, gcRequiredRef) {
199 var deleteAtomState = function deleteAtomState(prevState, dependent) {
200 prevState.forEach(function (_atomState, atom) {
201 deleteDependent(dependentsMap, atom, dependent);
202 });
203 return new Map(prevState); // to re-render
204 };
205
206 gcRequiredRef.current = true;
207 setState(function (prev) {
208 return deleteAtomState(prev, id);
209 });
210 };
211
212 var gcAtom = function gcAtom(state, setState, dependentsMap) {
213 var gcAtomState = function gcAtomState(prevState) {
214 var nextState = new Map(prevState);
215
216 while (true) {
217 var deleted = false;
218 nextState.forEach(function (_atomState, atom) {
219 var _dependentsMap$get;
220
221 var isEmpty = ((_dependentsMap$get = dependentsMap.get(atom)) == null ? void 0 : _dependentsMap$get.size) === 0;
222
223 if (isEmpty) {
224 nextState.delete(atom);
225 dependentsMap.delete(atom);
226 deleted = true;
227 }
228 });
229
230 if (!deleted) {
231 break;
232 }
233 }
234
235 return nextState;
236 };
237
238 var nextState = gcAtomState(state);
239
240 if (state.size !== nextState.size) {
241 setState(nextState);
242 }
243 };
244
245 var writeAtom = function writeAtom(updatingAtom, update, dependentsMap, addWriteThunk) {
246 var updateDependentsState = function updateDependentsState(prevState, atom) {
247 var partialState = new Map();
248 listDependents(dependentsMap, atom).forEach(function (dependent) {
249 if (typeof dependent === 'symbol') return;
250 var v = dependent.read(function (a) {
251 if (a !== dependent) {
252 addDependent(dependentsMap, a, dependent);
253 }
254
255 return getAtomStateValue(prevState, a);
256 });
257
258 if (v instanceof Promise) {
259 var promise = v.then(function (vv) {
260 var nextAtomState = {
261 value: vv
262 };
263 addWriteThunk(function (prev) {
264 var nextState = new Map(prev).set(dependent, nextAtomState);
265 var nextPartialState = updateDependentsState(nextState, dependent);
266 return appendMap(nextState, nextPartialState);
267 });
268 }).catch(function (e) {
269 addWriteThunk(function (prev) {
270 return new Map(prev).set(dependent, {
271 value: getAtomStateValue(prev, dependent),
272 error: e instanceof Error ? e : new Error(e)
273 });
274 });
275 });
276 partialState.set(dependent, {
277 value: getAtomStateValue(prevState, dependent),
278 promise: promise
279 });
280 } else {
281 partialState.set(dependent, {
282 value: v
283 });
284 appendMap(partialState, updateDependentsState(concatMap(prevState, partialState), dependent));
285 }
286 });
287 return partialState;
288 };
289
290 var updateAtomState = function updateAtomState(prevState, atom, update) {
291 var partialState = new Map();
292 var isSync = true;
293
294 try {
295 var promise = atom.write(function (a) {
296 return getAtomStateValue(concatMap(prevState, partialState), a);
297 }, function (a, v) {
298 if (a === atom) {
299 var nextAtomState = {
300 value: v
301 };
302
303 if (isSync) {
304 partialState.set(a, nextAtomState);
305 appendMap(partialState, updateDependentsState(concatMap(prevState, partialState), a));
306 } else {
307 addWriteThunk(function (prev) {
308 var nextState = new Map(prev).set(a, nextAtomState);
309 var nextPartialState = updateDependentsState(nextState, a);
310 return appendMap(nextState, nextPartialState);
311 });
312 }
313 } else {
314 if (isSync) {
315 var nextPartialState = updateAtomState(prevState, a, v);
316 appendMap(partialState, nextPartialState);
317 } else {
318 addWriteThunk(function (prev) {
319 var nextPartialState = updateAtomState(prev, a, v);
320 return appendMap(new Map(prev), nextPartialState);
321 });
322 }
323 }
324 }, update);
325
326 if (promise instanceof Promise) {
327 var nextAtomState = {
328 value: getAtomStateValue(concatMap(prevState, partialState), atom),
329 promise: promise.then(function () {
330 addWriteThunk(function (prev) {
331 return new Map(prev).set(atom, {
332 value: getAtomStateValue(prev, atom),
333 promise: undefined
334 });
335 });
336 }).catch(function (e) {
337 addWriteThunk(function (prev) {
338 return new Map(prev).set(atom, {
339 value: getAtomStateValue(prev, atom),
340 error: e instanceof Error ? e : new Error(e)
341 });
342 });
343 })
344 };
345 partialState.set(atom, nextAtomState);
346 }
347 } catch (e) {
348 var _nextAtomState2 = {
349 value: getAtomStateValue(concatMap(prevState, partialState), atom),
350 error: e instanceof Error ? e : new Error(e)
351 };
352 partialState.set(atom, _nextAtomState2);
353 }
354
355 isSync = false;
356 return partialState;
357 };
358
359 addWriteThunk(function (prevState) {
360 var updatingAtomState = prevState.get(updatingAtom);
361
362 if (updatingAtomState && updatingAtomState.promise) {
363 // schedule update after promise is resolved
364 var promise = updatingAtomState.promise.then(function () {
365 var updateState = updateAtomState(prevState, updatingAtom, update);
366 addWriteThunk(function (prev) {
367 return appendMap(new Map(prev), updateState);
368 });
369 });
370 return new Map(prevState).set(updatingAtom, _extends({}, updatingAtomState, {
371 promise: promise
372 }));
373 } else {
374 var updateState = updateAtomState(prevState, updatingAtom, update);
375 return appendMap(new Map(prevState), updateState);
376 }
377 });
378 };
379
380 var runWriteThunk = function runWriteThunk(lastStateRef, setState, writeThunkQueue) {
381 while (true) {
382 if (lastStateRef.current === null) {
383 return;
384 }
385
386 if (writeThunkQueue.length === 0) {
387 return;
388 }
389
390 var thunk = writeThunkQueue.shift();
391 var lastState = lastStateRef.current;
392 var nextState = thunk(lastState);
393
394 if (nextState !== lastState) {
395 setState(nextState);
396 return;
397 }
398 }
399 };
400
401 var ActionsContext = useContextSelector.createContext(warningObject);
402 var StateContext = useContextSelector.createContext(warningObject);
403 var Provider = function Provider(_ref) {
404 var children = _ref.children;
405
406 var _useState = react.useState(initialState),
407 state = _useState[0],
408 setStateOrig = _useState[1];
409
410 var setState = function setState(setStateAction) {
411 lastStateRef.current = null;
412 setStateOrig(setStateAction);
413 };
414
415 var dependentsMapRef = react.useRef();
416
417 if (!dependentsMapRef.current) {
418 dependentsMapRef.current = new WeakMap();
419 }
420
421 var gcRequiredRef = react.useRef(false);
422 react.useEffect(function () {
423 if (!gcRequiredRef.current) {
424 return;
425 }
426
427 gcAtom(state, setState, dependentsMapRef.current);
428 gcRequiredRef.current = false;
429 }, [state]);
430 var lastStateRef = react.useRef(null);
431 useIsoLayoutEffect(function () {
432 lastStateRef.current = state;
433 });
434 var writeThunkQueueRef = react.useRef([]);
435 react.useEffect(function () {
436 runWriteThunk(lastStateRef, setState, writeThunkQueueRef.current);
437 }, [state]);
438 var actions = react.useMemo(function () {
439 return {
440 add: function add(id, atom, partialState) {
441 return addAtom(id, atom, partialState, setState, dependentsMapRef.current);
442 },
443 del: function del(id) {
444 return delAtom(id, setState, dependentsMapRef.current, gcRequiredRef);
445 },
446 read: function read(state, atom) {
447 return readAtom(state, atom, setState, dependentsMapRef.current);
448 },
449 write: function write(atom, update) {
450 writeAtom(atom, update, dependentsMapRef.current, function (thunk) {
451 writeThunkQueueRef.current.push(thunk);
452 runWriteThunk(lastStateRef, setState, writeThunkQueueRef.current);
453 });
454 }
455 };
456 }, []);
457 return react.createElement(ActionsContext.Provider, {
458 value: actions
459 }, react.createElement(StateContext.Provider, {
460 value: state
461 }, children));
462 };
463
464 var keyCount = 0; // global key count for all atoms
465
466 function atom(read, write) {
467 var config = {
468 key: ++keyCount
469 };
470
471 if (typeof read === 'function') {
472 config.read = read;
473 } else {
474 config.init = read;
475
476 config.read = function (get) {
477 return get(config);
478 };
479
480 config.write = function (get, set, update) {
481 set(config, typeof update === 'function' ? update(get(config)) : update);
482 };
483 }
484
485 if (write) {
486 config.write = write;
487 }
488
489 return config;
490 }
491
492 var isWritable = function isWritable(atom) {
493 return !!atom.write;
494 };
495
496 function useAtom(atom) {
497 var actions = useContextSelector.useContext(ActionsContext);
498 var pendingListRef = react.useRef([]);
499 var value = useContextSelector.useContextSelector(StateContext, react.useCallback(function (state) {
500 var atomState = state.get(atom);
501
502 if (!atomState) {
503 var _actions$read = actions.read(state, atom),
504 initialAtomState = _actions$read[0],
505 pendingPartialState = _actions$read[1];
506
507 atomState = initialAtomState;
508
509 if (!atomState.error && !atomState.promise && pendingPartialState.size) {
510 pendingListRef.current.unshift({
511 v: initialAtomState.value,
512 p: pendingPartialState
513 });
514 }
515 }
516
517 if (atomState.error) {
518 throw atomState.error;
519 }
520
521 if (atomState.promise) {
522 throw atomState.promise;
523 }
524
525 return atomState.value;
526 }, [atom, actions]));
527 var pendingPartialStateRef = react.useRef();
528 useIsoLayoutEffect(function () {
529 var pendingList = pendingListRef.current;
530 var found = pendingList.find(function (_ref) {
531 var v = _ref.v;
532 return v === value;
533 });
534
535 if (found) {
536 pendingPartialStateRef.current = found.p;
537 }
538
539 pendingList.splice(0, pendingList.length);
540 });
541 useIsoLayoutEffect(function () {
542 var id = Symbol();
543 actions.add(id, atom, pendingPartialStateRef.current);
544 return function () {
545 actions.del(id);
546 };
547 }, [actions, atom]);
548 var setAtom = react.useCallback(function (update) {
549 if (isWritable(atom)) {
550 actions.write(atom, update);
551 } else {
552 throw new Error('not writable atom');
553 }
554 }, [atom, actions]);
555 react.useDebugValue(value);
556 return [value, setAtom];
557 }
558
559 exports.Provider = Provider;
560 exports.atom = atom;
561 exports.useAtom = useAtom;
562
563 return exports;
564
565}({}, React, useContextSelector));