Created
September 5, 2021 19:39
-
-
Save mukaschultze/47f11a033bd44bf3ec95e433bad01d04 to your computer and use it in GitHub Desktop.
RxJS hooks for React
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 { useEffect, useRef, useState } from "react"; | |
import { BehaviorSubject, Observable, PartialObserver, Subject } from "rxjs"; | |
function useConstant<T>(factory: () => T): T { | |
const ref = useRef<{ value: T }>(); | |
if (!ref.current) { | |
ref.current = { value: factory() }; | |
} | |
return ref.current.value; | |
} | |
/** Create an Observable from an observable factory */ | |
export function useObservable<T>(observableFactory: () => Observable<T>) { | |
return useConstant(observableFactory); | |
} | |
/** Subscribe to an Observable and return an stateful tuple containing the last | |
* value emitted, the error (if any) and a boolean indicating if the Observable | |
* has completed. | |
*/ | |
export function useSubscription<T>( | |
observableFactory: () => Observable<T> | |
): [T | null, any | undefined, boolean]; | |
export function useSubscription<T>( | |
observableFactory: () => Observable<T>, | |
defaultValue?: T | (() => T) | |
): [T, any | undefined, boolean]; | |
export function useSubscription<T>( | |
observableFactory: () => Observable<T>, | |
defaultValue?: T | (() => T) | |
) { | |
const [value, setValue] = useState<T | null>(defaultValue ?? null); | |
const [error, setError] = useState<any | undefined>(undefined); | |
const [complete, setComplete] = useState<boolean>(false); | |
useSubscribe(observableFactory, { | |
next: (value) => setValue(value), | |
error: (error) => setError(error), | |
complete: () => setComplete(true), | |
}); | |
return [value, error, complete] as const; | |
} | |
/** Subscribe to the emissions of an Observable, similar to useEffect */ | |
export function useSubscribe<T>( | |
observableFactory: () => Observable<T>, | |
partialObserver?: PartialObserver<T> | |
) { | |
const observable = useObservable(observableFactory); | |
useEffect(() => { | |
const subscription = observable.subscribe(partialObserver); | |
return () => subscription.unsubscribe(); | |
}, [observable]); | |
} | |
/** Creates a new Subject that completes on component unmount */ | |
export function useSubject<T>() { | |
const sub = useConstant(() => new Subject<T>()); | |
useEffect(() => () => sub.complete(), [sub]); | |
return sub; | |
} | |
/** Returns a tuple containing a stateful value, a setter function and an | |
* BehaviorSubject, this is similar to using using useState | |
*/ | |
export function useBehaviorSubject<T>(defaultValue: T | (() => T)) { | |
const [value, setValue] = useState(defaultValue); | |
const sub = useConstant(() => new BehaviorSubject<T>(value)); | |
useEffect(() => () => sub.complete(), [sub]); | |
useSubscribe(() => sub, { next: setValue }); | |
return [value, sub.next, sub] as const; | |
} | |
/** Create an Observable that emits when a React dependency changes */ | |
export function useFromDependency<T>(dependency: T) { | |
const sub = useSubject<T>(); | |
const obs = useConstant(() => sub.asObservable()); | |
useEffect(() => sub.next(dependency), [dependency, sub]); | |
return obs; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment