Created
April 11, 2025 13:44
-
-
Save renatoaraujoc/cee9f56c4ad85cf129048451afb3ab1f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { catchError, map, type Observable, of, startWith } from 'rxjs'; | |
import { rxResource } from '@angular/core/rxjs-interop'; | |
import { | |
type Injector, | |
type ResourceRef, | |
type ResourceStatus, | |
type ValueEqualityFn | |
} from '@angular/core'; | |
type StateMachine<T> = | |
| { status: 'LOADING' } | |
| { status: 'OK'; data: T } | |
| { status: 'ERROR'; message: string }; | |
interface ResourceLoaderParams<R> { | |
request: Exclude<NoInfer<R>, undefined>; | |
abortSignal: AbortSignal; | |
previous: { | |
status: ResourceStatus; | |
}; | |
} | |
interface StateMachineResourceOptions<T, R> { | |
request?: () => R; | |
loader: (params: ResourceLoaderParams<R>) => Observable<T>; | |
equal?: ValueEqualityFn<T>; | |
injector?: Injector; | |
} | |
function createStateMachineEqualityFn<T>( | |
dataEqualityFn?: ValueEqualityFn<T> | |
): ValueEqualityFn<StateMachine<T>> { | |
return (a, b) => { | |
if (a.status !== b.status) { | |
return false; | |
} | |
if (a.status === 'LOADING') { | |
return true; | |
} | |
if (a.status === 'ERROR' && b.status === 'ERROR') { | |
return a.message === b.message; | |
} | |
if (a.status === 'OK' && b.status === 'OK') { | |
return dataEqualityFn | |
? dataEqualityFn(a.data, b.data) | |
: a.data === b.data; | |
} | |
return true; | |
}; | |
} | |
export function createStateMachineResource<T, R>( | |
params: StateMachineResourceOptions<T, R> | |
): ResourceRef<StateMachine<T>> { | |
return rxResource<StateMachine<T>, R>({ | |
request: params.request, | |
loader: (loaderParams) => | |
params.loader(loaderParams).pipe( | |
map((data) => ({ | |
status: 'OK' as const, | |
data | |
})), | |
catchError((err: Error) => | |
of({ | |
status: 'ERROR' as const, | |
message: err.message | |
}) | |
), | |
startWith({ status: 'LOADING' as const }) | |
), | |
defaultValue: { status: 'LOADING' as const }, | |
equal: createStateMachineEqualityFn(params.equal), | |
injector: params.injector | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment