import { useEffect, useState, type Dispatch, type SetStateAction, } from 'react'; import { ObservedValues } from './types'; import { mapObservablesToValues } from './mapObservablesToValues'; export function useObservers>(observables: T): ObservedValues { const [values, setValues] = useState(() => mapObservablesToValues(observables)); useEffect(() => { const unsubscribers = Object.keys(observables as {}).map((key) => { return observables[key].subscribe(((value: T[typeof key]) => { updateObservedValueIfChanged(setValues, key, value); })); }); reconcileStateAfterResume(setValues, observables); return () => unsubscribers.forEach(unsubscribe => unsubscribe()); }, []); return values; } function updateObservedValueIfChanged>( setValues: Dispatch>>, key: string, value: T[string], ) { setValues((currentValues) => ( Object.is(currentValues[key], value) ? currentValues : { ...currentValues, [key]: value } )); } function reconcileStateAfterResume>( setValues: Dispatch>>, observables: T, ) { setValues((currentValues) => { const nextValues = mapObservablesToValues(observables); return haveObservedValuesChanged(currentValues, nextValues) ? nextValues : currentValues; }); } function haveObservedValuesChanged>( currentValues: ObservedValues, nextValues: ObservedValues, ) { return (Object.keys(nextValues as {}) as Array).some((key) => ( !Object.is(currentValues[key], nextValues[key]) )); }