import { useEffect, useReducer } from 'react' export type ReactiveVar = { (newValue?: T | ((value: T) => T)): T subscribe: (handler: Function) => () => void unsubscribe: (handler: Function) => void } type EqualsFunc = (a: T, b: T) => boolean export const makeVar = ( initialValue: T, equalsFunc?: EqualsFunc, ): ReactiveVar => { let value = initialValue const subscribers = new Set() const reactiveVar = (newValue?: T | ((value: T) => T)) => { if (newValue !== undefined) { let nextValue = value let valueChanged if (newValue instanceof Function) { nextValue = newValue(value) } else { nextValue = newValue } valueChanged = equalsFunc ? !equalsFunc(nextValue, value) : nextValue !== value value = nextValue if (valueChanged) { subscribers.forEach((handler) => handler(value)) } } return value } reactiveVar.subscribe = (handler: Function) => { subscribers.add(handler) return () => subscribers.delete(handler) } reactiveVar.unsubscribe = (handler: Function) => { subscribers.delete(handler) } return reactiveVar } export const useReactiveVar = ( reactiveVar: ReactiveVar, ) => { const handler = useReducer((x) => x + 1, 0)[1] as () => void useEffect(() => { reactiveVar.subscribe(handler) return () => { reactiveVar.unsubscribe(handler) } }, []) return reactiveVar() }