diff --git a/packages/core/src/bind/connectFactoryObservable.ts b/packages/core/src/bind/connectFactoryObservable.ts index 1cec3b7..bce4ba4 100644 --- a/packages/core/src/bind/connectFactoryObservable.ts +++ b/packages/core/src/bind/connectFactoryObservable.ts @@ -1,7 +1,7 @@ import { Observable } from "rxjs" import { EMPTY_VALUE } from "../internal/empty-value" import { state, StateObservable, SUSPENSE } from "@rx-state/core" -import { useStateObservable } from "../useStateObservable" +import { useStateObservable } from "../" /** * Accepts: A factory function that returns an Observable. diff --git a/packages/core/src/bind/connectObservable.ts b/packages/core/src/bind/connectObservable.ts index 6c6c241..45206e6 100644 --- a/packages/core/src/bind/connectObservable.ts +++ b/packages/core/src/bind/connectObservable.ts @@ -1,6 +1,6 @@ import { EMPTY_VALUE } from "../internal/empty-value" import { Observable } from "rxjs" -import { useStateObservable } from "../useStateObservable" +import { useStateObservable } from "../" import { state } from "@rx-state/core" /** diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 7068462..cdb390f 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -1,5 +1,19 @@ -export * from "@rx-state/core" -export { shareLatest } from "./shareLatest" -export { useStateObservable } from "./useStateObservable" +export { + AddStopArg, + DefaultedStateObservable, + EmptyObservableError, + liftSuspense, + NoSubscribersError, + PipeState, + sinkSuspense, + StateObservable, + StatePromise, + SUSPENSE, + withDefault, + WithDefaultOperator, +} from "@rx-state/core" export { bind } from "./bind" -export { Subscribe, RemoveSubscribe } from "./Subscribe" +export { shareLatest } from "./shareLatest" +export { state } from "./stateJsx" +export { RemoveSubscribe, Subscribe } from "./Subscribe" +export { useStateObservable } from "./useStateObservable" diff --git a/packages/core/src/stateJsx.test.tsx b/packages/core/src/stateJsx.test.tsx new file mode 100644 index 0000000..164c242 --- /dev/null +++ b/packages/core/src/stateJsx.test.tsx @@ -0,0 +1,47 @@ +import { render, screen, act } from "@testing-library/react" +import React, { Suspense } from "react" +import { map, Subject } from "rxjs" +import { state } from "./" + +describe("stateJsx", () => { + it("is possible to use StateObservables as JSX elements", async () => { + const subject = new Subject() + const state$ = state(subject) + const subscription = state$.subscribe() + + render({state$}) + + expect(screen.queryByText("Result")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + await act(() => { + subject.next("Result") + return Promise.resolve() + }) + + expect(screen.queryByText("Result")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + subscription.unsubscribe() + }) + + it("is possible to use factory StateObservables as JSX elements", async () => { + const subject = new Subject() + const state$ = state((value: string) => subject.pipe(map((x) => value + x))) + + const subscription = state$("hello ").subscribe() + + render({state$("hello ")}) + + expect(screen.queryByText("hello world!")).toBeNull() + expect(screen.queryByText("Waiting")).not.toBeNull() + + await act(() => { + subject.next("world!") + return Promise.resolve() + }) + + expect(screen.queryByText("hello world!")).not.toBeNull() + expect(screen.queryByText("Waiting")).toBeNull() + subscription.unsubscribe() + }) +}) diff --git a/packages/core/src/stateJsx.tsx b/packages/core/src/stateJsx.tsx new file mode 100644 index 0000000..c856943 --- /dev/null +++ b/packages/core/src/stateJsx.tsx @@ -0,0 +1,27 @@ +import { state as coreState, StateObservable } from "@rx-state/core" +import React, { createElement, ReactElement } from "react" +import { useStateObservable } from "./useStateObservable" + +declare module "@rx-state/core" { + interface StateObservable extends ReactElement {} +} + +export const state: typeof coreState = (...args: any[]): any => { + const result = (coreState as any)(...args) + + if (typeof result === "function") { + return (...args: any[]) => enhanceState(result(...args)) + } + return enhanceState(result) +} + +const cache = new WeakMap, React.ReactNode>() +function enhanceState(state$: StateObservable) { + if (!cache.has(state$)) + cache.set( + state$, + createElement(() => useStateObservable(state$) as any, {}), + ) + + return Object.assign(state$, cache.get(state$)!) +}