diff --git a/src/react-bindings/reactRx.tsx b/src/react-bindings/reactRx.tsx index 5c42704..9cb7116 100644 --- a/src/react-bindings/reactRx.tsx +++ b/src/react-bindings/reactRx.tsx @@ -1,6 +1,6 @@ import type { Observable, Subscription } from 'rxjs'; import { RediError } from '@wendellhu/redi'; -import { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; type ObservableOrFn = Observable | (() => Observable); type Nullable = T | undefined | null; @@ -51,67 +51,48 @@ export function useObservable( ); } - const observableRef = useRef | null>(null); - const receivedSyncValueRef = useRef(false); - const subscribedRef = useRef(false); - const destObservable = useMemo( () => observable, [...(typeof deps !== 'undefined' ? deps : [observable])], ); - // This state is only for trigger React to re-render. We do not use `setValue` directly because it may cause - // memory leaking. - const [_, setRenderCounter] = useState(0); - - const valueRef = useRef( - (() => { - let innerDefaultValue: T | undefined; - if (destObservable) { - const sub = unwrap(destObservable).subscribe((value) => { - receivedSyncValueRef.current = true; - innerDefaultValue = value; - }); - - sub.unsubscribe(); - } + const observableRef = useRef>>(undefined); + const subscriptionRef = useRef(null); + const syncReceivedValueRef = useRef(false); - return innerDefaultValue ?? defaultValue; - })(), - ); + // This state is only for trigger React to re-render. We do not use `setValue` + // directly because it may cause memory leaking in React. + const [_, setRenderCounter] = useState(0); + const valueRef = useRef(defaultValue ?? undefined); - useLayoutEffect(() => { - subscribedRef.current = false; + if (observableRef.current !== destObservable) { + if (subscriptionRef.current) { + subscriptionRef.current.unsubscribe(); + subscriptionRef.current = null; + } - let subscription: Subscription | null = null; + observableRef.current = destObservable; if (destObservable) { - observableRef.current = unwrap(destObservable); - subscription = observableRef.current.subscribe((value) => { - if ( - receivedSyncValueRef.current && - !subscribedRef.current && - value === valueRef.current - ) { - subscribedRef.current = true; - return; - } - - subscribedRef.current = true; + subscriptionRef.current = unwrap(destObservable).subscribe((value) => { valueRef.current = value; - setRenderCounter((prev) => prev + 1); + if (syncReceivedValueRef.current === false) { + syncReceivedValueRef.current = true; + } else { + setRenderCounter((prev) => (prev + 1) % Number.MAX_SAFE_INTEGER); + } }); } + } - return () => subscription?.unsubscribe(); - }, [destObservable]); - - if (shouldHaveSyncValue && !receivedSyncValueRef.current) { + if (shouldHaveSyncValue && !syncReceivedValueRef.current) { throw new Error( '[redi]: Expect `shouldHaveSyncValue` but not getting a sync value!', ); } + syncReceivedValueRef.current = true; + return valueRef.current; }