@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
JavaScript
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);
}
}