deno.land / x / jotai@v1.8.4 / src / utils / atomWithObservable.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156import { atom } from 'jotai'import type { Atom, Getter, WritableAtom } from 'jotai'
type Timeout = ReturnType<typeof setTimeout>type AnyError = unknown
declare global { interface SymbolConstructor { readonly observable: symbol }}
type Subscription = { unsubscribe: () => void}
type Observer<T> = { next: (value: T) => void error: (error: AnyError) => void complete: () => void}
type ObservableLike<T> = { subscribe(observer: Observer<T>): Subscription subscribe( next: (value: T) => void, error?: (error: AnyError) => void, complete?: () => void ): Subscription [Symbol.observable]?: () => ObservableLike<T> | undefined}
type SubjectLike<T> = ObservableLike<T> & Observer<T>
type Options<Data> = { initialValue?: Data | (() => Data) unstable_timeout?: number}
export function atomWithObservable<Data>( createObservable: (get: Getter) => SubjectLike<Data>, options?: Options<Data>): WritableAtom<Data, Data>
export function atomWithObservable<Data>( createObservable: (get: Getter) => ObservableLike<Data>, options?: Options<Data>): Atom<Data>
export function atomWithObservable<Data>( createObservable: (get: Getter) => ObservableLike<Data> | SubjectLike<Data>, options?: Options<Data>) { const observableResultAtom = atom((get) => { let observable = createObservable(get) const itself = observable[Symbol.observable]?.() if (itself) { observable = itself }
type Result = { d: Data } | { e: AnyError } let resolve: ((result: Result) => void) | undefined const makePending = () => new Promise<Result>((r) => { resolve = r }) const initialResult: Result | Promise<Result> = options && 'initialValue' in options ? { d: typeof options.initialValue === 'function' ? (options.initialValue as () => Data)() : (options.initialValue as Data), } : makePending()
let setResult: ((result: Result) => void) | undefined let lastResult: Result | undefined const listener = (result: Result) => { lastResult = result resolve?.(result) setResult?.(result) }
let subscription: Subscription | undefined let timer: Timeout | undefined const isNotMounted = () => !setResult const start = () => { if (subscription) { clearTimeout(timer) subscription.unsubscribe() } subscription = observable.subscribe( (d) => listener({ d }), (e) => listener({ e }) ) if (isNotMounted() && options?.unstable_timeout) { timer = setTimeout(() => { if (subscription) { subscription.unsubscribe() subscription = undefined } }, options.unstable_timeout) } } start()
const resultAtom = atom(lastResult || initialResult) resultAtom.onMount = (update) => { setResult = update if (lastResult) { update(lastResult) } if (subscription) { clearTimeout(timer) } else { start() } return () => { setResult = undefined if (subscription) { subscription.unsubscribe() subscription = undefined } } } return [resultAtom, observable, makePending, start, isNotMounted] as const })
const observableAtom = atom( (get) => { const [resultAtom] = get(observableResultAtom) const result = get(resultAtom) if ('e' in result) { throw result.e } return result.d }, (get, set, data: 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}
Version Info