jotai
Version:
👻 Primitive and flexible state management for React
110 lines (109 loc) • 3.7 kB
JavaScript
import { atom } from '../../vanilla.js';
const isPromiseLike = (x) => typeof x?.then === 'function';
const getSymbolObservable = () => Symbol.observable;
export function atomWithObservable(getObservable, options) {
const returnResultData = (result) => {
if ('e' in result) {
throw result.e;
}
return result.d;
};
const observableResultAtom = atom((get) => {
const observable = getObservable(get);
const symbolObservable = getSymbolObservable() ?? 'undefined';
const subscribable = observable[symbolObservable]?.() ||
observable;
let resolve;
const makePending = () => new Promise((r) => {
resolve = r;
});
const initialResult = options && 'initialValue' in options
? {
d: typeof options.initialValue === 'function'
? options.initialValue()
: options.initialValue,
}
: makePending();
let setResult;
let lastResult;
const listener = (result) => {
lastResult = result;
resolve?.(result);
setResult?.(result);
};
let subscription;
let timer;
const isNotMounted = () => !setResult;
const unsubscribe = () => {
if (subscription) {
subscription.unsubscribe();
subscription = undefined;
}
};
const start = () => {
if (subscription) {
clearTimeout(timer);
subscription.unsubscribe();
}
subscription = subscribable.subscribe({
next: (d) => listener({ d }),
error: (e) => listener({ e }),
complete: () => { },
});
if (isNotMounted() && options?.unstable_timeout) {
timer = setTimeout(unsubscribe, options.unstable_timeout);
}
};
start();
const resultAtom = atom(lastResult || initialResult);
if (process.env.NODE_ENV !== 'production') {
resultAtom.debugPrivate = true;
}
resultAtom.onMount = (update) => {
setResult = update;
if (lastResult) {
update(lastResult);
}
if (subscription) {
clearTimeout(timer);
}
else {
start();
}
return () => {
setResult = undefined;
if (options?.unstable_timeout) {
timer = setTimeout(unsubscribe, options.unstable_timeout);
}
else {
unsubscribe();
}
};
};
return [resultAtom, observable, makePending, start, isNotMounted];
});
if (process.env.NODE_ENV !== 'production') {
observableResultAtom.debugPrivate = true;
}
const observableAtom = atom((get) => {
const [resultAtom] = get(observableResultAtom);
const result = get(resultAtom);
if (isPromiseLike(result)) {
return result.then(returnResultData);
}
return returnResultData(result);
}, (get, set, data) => {
const [resultAtom, observable, makePending, start, isNotMounted] = get(observableResultAtom);
if ('next' in observable) {
if (isNotMounted()) {
set(resultAtom, makePending());
start();
}
observable.next(data);
}
else {
throw new Error('observable is not subject');
}
});
return observableAtom;
}