diff --git a/packages/core/src/Subscribe.test.tsx b/packages/core/src/Subscribe.test.tsx
index 1fef0d0..1353926 100644
--- a/packages/core/src/Subscribe.test.tsx
+++ b/packages/core/src/Subscribe.test.tsx
@@ -1,6 +1,8 @@
-import { render } from "@testing-library/react"
+import { state } from "@josepot/rxjs-state"
+import { render, screen } from "@testing-library/react"
import React, { StrictMode, useState } from "react"
-import { defer, Observable, of } from "rxjs"
+import { defer, EMPTY, Observable, of, startWith } from "rxjs"
+import { useStateObservable } from "./useStateObservable"
import { bind, Subscribe as OriginalSubscribe } from "./"
const Subscribe = (props: any) => {
@@ -13,6 +15,24 @@ const Subscribe = (props: any) => {
describe("Subscribe", () => {
describe("Subscribe with source$", () => {
+ it("renders the sync emitted value on a StateObservable without default value", () => {
+ const test$ = state(EMPTY.pipe(startWith("there!")))
+ const useTest = () => useStateObservable(test$)
+
+ const Test: React.FC = () => <>Hello {useTest()}>
+
+ const TestSubscribe: React.FC = () => (
+
+
+
+ )
+
+ const { unmount } = render()
+
+ expect(screen.queryByText("Hello there!")).not.toBeNull()
+
+ unmount()
+ })
it("subscribes to the provided observable and remains subscribed until it's unmounted", () => {
let nSubscriptions = 0
const [useNumber, number$] = bind(
diff --git a/packages/core/src/Subscribe.tsx b/packages/core/src/Subscribe.tsx
index 95e9ddb..2487505 100644
--- a/packages/core/src/Subscribe.tsx
+++ b/packages/core/src/Subscribe.tsx
@@ -9,7 +9,7 @@ import React, {
} from "react"
import { Observable, Subscription } from "rxjs"
-const SubscriptionContext = createContext(null as any)
+const SubscriptionContext = createContext(null)
const { Provider } = SubscriptionContext
export const useSubscription = () => useContext(SubscriptionContext)
diff --git a/packages/core/src/bind/connectFactoryObservable.ts b/packages/core/src/bind/connectFactoryObservable.ts
index 981b11f..2ba0953 100644
--- a/packages/core/src/bind/connectFactoryObservable.ts
+++ b/packages/core/src/bind/connectFactoryObservable.ts
@@ -1,9 +1,8 @@
-import { noop, Observable } from "rxjs"
-import { useObservable } from "../internal/useObservable"
+import { Observable } from "rxjs"
import { SUSPENSE } from "../SUSPENSE"
import { EMPTY_VALUE } from "../internal/empty-value"
-import { useSubscription } from "../Subscribe"
import { state, StateObservable } from "@josepot/rxjs-state"
+import { useStateObservable } from "../useStateObservable"
/**
* Accepts: A factory function that returns an Observable.
@@ -39,9 +38,5 @@ export default function connectFactoryObservable(
: [getObservable, defaultValue]
const obs = state(...(args as [(...args: A) => Observable]))
- const useSub = defaultValue === EMPTY_VALUE ? useSubscription : noop
- return [
- (...input: A) => useObservable(obs(...input) as any, useSub() as any),
- obs,
- ]
+ return [(...input: A) => useStateObservable(obs(...input)), obs]
}
diff --git a/packages/core/src/bind/connectObservable.ts b/packages/core/src/bind/connectObservable.ts
index 03eacc9..9247b68 100644
--- a/packages/core/src/bind/connectObservable.ts
+++ b/packages/core/src/bind/connectObservable.ts
@@ -1,7 +1,6 @@
import { EMPTY_VALUE } from "../internal/empty-value"
-import { noop, Observable } from "rxjs"
-import { useSubscription } from "../Subscribe"
-import { useObservable } from "../internal/useObservable"
+import { Observable } from "rxjs"
+import { useStateObservable } from "../useStateObservable"
import { state } from "@josepot/rxjs-state"
/**
@@ -23,13 +22,11 @@ export default function connectObservable(
observable: Observable,
defaultValue: T,
) {
- const useSub = defaultValue === EMPTY_VALUE ? useSubscription : noop
const sharedObservable$ =
defaultValue === EMPTY_VALUE
? state(observable)
: state(observable, defaultValue)
- const useStaticObservable = () =>
- useObservable(sharedObservable$ as any, useSub() as any)
+ const useStaticObservable = () => useStateObservable(sharedObservable$ as any)
return [useStaticObservable, sharedObservable$] as const
}
diff --git a/packages/core/src/internal/useObservable.ts b/packages/core/src/internal/useObservable.ts
deleted file mode 100644
index bdb1e66..0000000
--- a/packages/core/src/internal/useObservable.ts
+++ /dev/null
@@ -1,73 +0,0 @@
-import { Subscription } from "rxjs"
-import { useRef, useState } from "react"
-import { SUSPENSE, filterOutSuspense } from "../SUSPENSE"
-import { DefaultedStateObservable, StateObservable } from "@josepot/rxjs-state"
-import { EMPTY_VALUE } from "./empty-value"
-import useSyncExternalStore from "./useSyncExternalStore"
-
-type VoidCb = () => void
-
-interface Ref {
- source$: StateObservable
- args: [(cb: VoidCb) => VoidCb, () => Exclude]
-}
-
-export const useObservable = (
- source$: StateObservable,
- subscription?: Subscription,
-): Exclude => {
- const [, setError] = useState()
- const callbackRef = useRef[>()
-
- if (!callbackRef.current) {
- const getValue = (src: StateObservable) => {
- const result = src.getValue(filterOutSuspense)
- if (result instanceof Promise) throw result
- return result as any
- }
-
- const gv: () => Exclude = () => {
- const src = callbackRef.current!.source$ as DefaultedStateObservable
-
- if (src.getRefCount() > 0 || src.getDefaultValue) return getValue(src)
-
- if (!subscription) throw new Error("Missing Subscribe!")
-
- let error = EMPTY_VALUE
- subscription.add(
- src.subscribe({
- error: (e) => {
- error = e
- },
- }),
- )
- if (error !== EMPTY_VALUE) throw error
- return getValue(src)
- }
-
- callbackRef.current = {
- source$: null as any,
- args: [, gv] as any,
- }
- }
-
- const ref = callbackRef.current
- if (ref.source$ !== source$) {
- ref.source$ = source$
- ref.args[0] = (next: () => void) => {
- const subscription = source$.subscribe({
- next,
- error: (e) => {
- setError(() => {
- throw e
- })
- },
- })
- return () => {
- subscription.unsubscribe()
- }
- }
- }
-
- return useSyncExternalStore(...ref!.args)
-}
diff --git a/packages/core/src/useStateObservable.ts b/packages/core/src/useStateObservable.ts
index 58ef5a4..d2c8b51 100644
--- a/packages/core/src/useStateObservable.ts
+++ b/packages/core/src/useStateObservable.ts
@@ -1,7 +1,73 @@
-import { useObservable } from "./internal/useObservable"
-import { SUSPENSE } from "./SUSPENSE"
-import type { StateObservable } from "@josepot/rxjs-state"
+import { useRef, useState } from "react"
+import { SUSPENSE, filterOutSuspense } from "./SUSPENSE"
+import { DefaultedStateObservable, StateObservable } from "@josepot/rxjs-state"
+import { EMPTY_VALUE } from "./internal/empty-value"
+import useSyncExternalStore from "./internal/useSyncExternalStore"
+import { useSubscription } from "./Subscribe"
-export const useStateObservable = (
- stateObservable: StateObservable,
-): Exclude => useObservable(stateObservable)
+type VoidCb = () => void
+
+interface Ref {
+ source$: StateObservable
+ args: [(cb: VoidCb) => VoidCb, () => Exclude]
+}
+
+export const useStateObservable = (
+ source$: StateObservable,
+): Exclude => {
+ const subscription = useSubscription()
+ const [, setError] = useState()
+ const callbackRef = useRef][>()
+
+ if (!callbackRef.current) {
+ const getValue = (src: StateObservable) => {
+ const result = src.getValue(filterOutSuspense)
+ if (result instanceof Promise) throw result
+ return result as any
+ }
+
+ const gv: () => Exclude = () => {
+ const src = callbackRef.current!.source$ as DefaultedStateObservable
+
+ if (src.getRefCount() > 0 || src.getDefaultValue) return getValue(src)
+
+ if (!subscription) throw new Error("Missing Subscribe!")
+
+ let error = EMPTY_VALUE
+ subscription.add(
+ src.subscribe({
+ error: (e) => {
+ error = e
+ },
+ }),
+ )
+ if (error !== EMPTY_VALUE) throw error
+ return getValue(src)
+ }
+
+ callbackRef.current = {
+ source$: null as any,
+ args: [, gv] as any,
+ }
+ }
+
+ const ref = callbackRef.current
+ if (ref.source$ !== source$) {
+ ref.source$ = source$
+ ref.args[0] = (next: () => void) => {
+ const subscription = source$.subscribe({
+ next,
+ error: (e) => {
+ setError(() => {
+ throw e
+ })
+ },
+ })
+ return () => {
+ subscription.unsubscribe()
+ }
+ }
+ }
+
+ return useSyncExternalStore(...ref!.args)
+}
]