/* eslint-disable no-param-reassign */ import { useCallback, useEffect, useMemo, useState, type Dispatch, } from 'react'; import { Observable } from './Observable'; type ObservableOrGenerator = Observable | (() => Observable); export function useObserver(observableOrGenerator: ObservableOrGenerator): [T, (next: T) => void] { const observable = useMemo( () => getOrGenerateObservable(observableOrGenerator), [], ); const [value, setValue] = useState(observable.value); const onNext = useCallback((next: T) => { observable.value = next; }, [observable]); useEffect(() => { const unsubscribe = observable.subscribe(setValue); reconcileStateWithObservableAfterResume(setValue, observable); return unsubscribe; }, [observable]); return [value, onNext]; } function reconcileStateWithObservableAfterResume(setValue: Dispatch, observable: Observable) { setValue((currentValue: T) => ( Object.is(currentValue, observable.value) ? currentValue : observable.value )); } function getOrGenerateObservable(observableOrGenerator: ObservableOrGenerator) { return observableOrGenerator instanceof Observable ? observableOrGenerator : observableOrGenerator(); }