rx-store-core
Version:
A Rxjs and Immutable based Type-safe state management tool
124 lines (113 loc) • 3.49 kB
text/typescript
import { BehaviorSubject, catchError, exhaustMap, from, map, of, switchMap, tap } from "rxjs";
import {
Computed,
Computation,
BS,
Connectivity,
Comparator,
ComputationAsync,
ComputedAsync,
AsyncResponse,
AsyncStates,
} from "rx-store-types";
import { bound } from "./decorators/bound";
export class ComputedImpl<R, S extends BS> implements Computed<R, S> {
readonly computation: Computation<R, S>;
private $source: BehaviorSubject<R>;
constructor(
computation: Computation<R, S>,
private subscribable: Connectivity<S>,
private comparator?: Comparator<{ [K in keyof S]: ReturnType<S[K]> }>
) {
this.computation = computation;
this.$source = new BehaviorSubject<R>(this.computation(subscribable.getDefaultAll()))
}
private setComputed(states: { [K in keyof S]: ReturnType<S[K]> }) {
const value = this.computation(states);
this.$source.next(value);
}
get() {
return this.$source.value;
}
observe(observer: (r: R) => void) {
const subscription = this.$source.subscribe(observer);
const unObserveAll = this.subscribable.observeAll(this.setComputed, this.comparator);
return () => {
subscription.unsubscribe();
unObserveAll();
}
}
}
export class ComputedAsyncImpl<R, S extends BS> implements ComputedAsync<R, S> {
readonly computation: ComputationAsync<R, S>;
private computed?: R;
private state: AsyncStates = AsyncStates.PENDING;
constructor(
computation: ComputationAsync<R, S>,
private subscribable: Connectivity<S>,
private lazy: boolean,
private onStart?: (val: { [K in keyof S]: ReturnType<S[K]> }) => void,
private onError?: (err: any) => void,
private onSuccess?: (result: R) => void,
private onComplete?: () => void
) {
this.computation = computation;
}
private getObservable(states: { [K in keyof S]: ReturnType<S[K]> }) {
const asyncReturn = this.computation(states);
return asyncReturn instanceof Promise ? from(asyncReturn) : asyncReturn;
}
get() {
return {
state: this.state,
value: this.computed,
};
}
observe(observer: (r: AsyncResponse<R>) => void, onPending?: Function) {
const connect = this.lazy ? exhaustMap : switchMap;
const subscription = this.subscribable
.source()
.pipe(
tap((val) => {
this.state = AsyncStates.PENDING;
onPending?.();
this.onStart?.(val);
}),
connect((states) => {
return this.getObservable(states).pipe(
map((result) => {
return {
success: true,
result,
} as const;
}),
catchError((err) => {
return of({
success: false,
cause: err,
} as const);
}),
tap((deferred) => {
if (deferred.success) {
this.state = AsyncStates.FULFILLED;
this.onSuccess?.(deferred.result);
return;
}
this.onError?.(deferred.cause);
this.state = AsyncStates.ERROR;
})
);
})
)
.subscribe({
next: observer,
complete: this.onComplete,
});
return () => subscription.unsubscribe();
}
}