UNPKG

@mittwald/react-use-promise

Version:

Simple and declarative use of Promises in your React components. Observe their state and refresh them in various advanced ways.

84 lines (83 loc) 2.89 kB
import { ConsolidatedTimeout, } from "../lib/ConsolidatedTimeout.js"; import { emptyValue, setValue } from "../lib/EventualValue.js"; import { ObservableValue } from "../observable-value/ObservableValue.js"; import { useWatchObservableValue } from "../observable-value/useWatchObservableValue.js"; import { useWatchResourceValue } from "./useWatchResourceValue.js"; export class AsyncResource { loader; loaderPromise; loaderPromiseVersion = 0; autoRefreshTimeout; value = new ObservableValue(emptyValue); valueWithCache = new ObservableValue(emptyValue); error = new ObservableValue(emptyValue); state = new ObservableValue("void"); onRefreshListeners = new Set(); static voidInstance = new AsyncResource(() => Promise.resolve(undefined)); constructor(loader) { this.loader = loader; this.autoRefreshTimeout = new ConsolidatedTimeout(() => this.refresh()); } refresh() { this.loaderPromiseVersion++; this.loaderPromise = undefined; this.value.updateValue(emptyValue); this.error.updateValue(emptyValue); this.state.updateValue("void"); this.onRefreshListeners.forEach((listener) => listener()); } onRefresh(handler) { this.onRefreshListeners.add(handler); return () => { this.onRefreshListeners.delete(handler); }; } addTTL(ttl) { return this.autoRefreshTimeout.addTimeout(ttl); } async load() { if (this.value.value.isSet || this.error.value.isSet) { return; } if (this.loaderPromise === undefined) { this.loaderPromise = this.handleLoading(); } } isMatchingError(error) { if (!this.error.value.isSet) { return false; } return error === true || this.error.value.value === error; } async handleLoading() { const loaderPromiseVersion = ++this.loaderPromiseVersion; let result = emptyValue; let error = emptyValue; this.state.updateValue("loading"); try { const awaitedResult = await this.loader(); result = setValue(awaitedResult); } catch (e) { error = setValue(e); } if (this.loaderPromiseVersion === loaderPromiseVersion) { if (result.isSet) { this.valueWithCache.updateValue(result); this.value.updateValue(result); this.state.updateValue("loaded"); } else if (error.isSet) { this.error.updateValue(error); this.state.updateValue("error"); } } this.autoRefreshTimeout.start(); } use(options = {}) { return useWatchResourceValue(this, options); } watchState() { return useWatchObservableValue(this.state); } }