From 20b7c7e582401c6b892b858495281ebd548c62a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 21 Jan 2025 02:36:49 +0100 Subject: [PATCH] useSuspenseScoped --- packages/example/src/routes/tests.tsx | 11 ++++-- packages/reffuse/src/Reffuse.ts | 52 +++++++++++++++++++-------- 2 files changed, 46 insertions(+), 17 deletions(-) diff --git a/packages/example/src/routes/tests.tsx b/packages/example/src/routes/tests.tsx index 1dee1f5..b36e30a 100644 --- a/packages/example/src/routes/tests.tsx +++ b/packages/example/src/routes/tests.tsx @@ -1,6 +1,6 @@ import { R } from "@/reffuse" import { createFileRoute } from "@tanstack/react-router" -import { Effect } from "effect" +import { Console, Effect } from "effect" export const Route = createFileRoute("/tests")({ @@ -12,8 +12,13 @@ function RouteComponent() { // Effect.map(() => "test") // )) - const value = R.useSuspense(Effect.succeed("test")) - console.log(value) + R.useSuspenseScoped(Effect.addFinalizer(() => Console.log("cleanup")).pipe( + Effect.andThen(Effect.promise(() => new Promise(resolve => + resolve("test") + ))), + + Effect.tap(Console.log), + ), []) return
Hello "/tests"!
} diff --git a/packages/reffuse/src/Reffuse.ts b/packages/reffuse/src/Reffuse.ts index 0f9acdc..83c5df4 100644 --- a/packages/reffuse/src/Reffuse.ts +++ b/packages/reffuse/src/Reffuse.ts @@ -226,20 +226,6 @@ export class Reffuse { ]) } - useSuspense( - effect: Effect.Effect, - deps?: React.DependencyList, - options?: { readonly signal?: AbortSignal } & RenderOptions, - ): A { - const runPromise = this.useRunPromise() - - const promise = React.useMemo(() => runPromise(effect, options), [ - ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runPromise], - ...(deps ?? []), - ]) - return React.use(promise) - } - /** * An asynchronous and non-blocking alternative to `React.useEffect`. * @@ -291,6 +277,44 @@ export class Reffuse { ]) } + // useSuspense( + // effect: Effect.Effect, + // deps?: React.DependencyList, + // options?: { readonly signal?: AbortSignal } & RenderOptions, + // ): A { + // const runPromise = this.useRunPromise() + + // const promise = React.useMemo(() => runPromise(effect, options), [ + // ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runPromise], + // ...(deps ?? []), + // ]) + // return React.use(promise) + // } + + useSuspenseScoped( + effect: Effect.Effect, + deps?: React.DependencyList, + options?: { readonly signal?: AbortSignal } & RenderOptions & ScopeOptions, + ): A { + const runSync = this.useRunSync() + const runPromise = this.useRunPromise() + + const initialPromise = React.useMemo(() => runPromise(Effect.scoped(effect)), []) + const [promise, setPromise] = React.useState(initialPromise) + + React.useEffect(() => { + const scope = runSync(Scope.make()) + setPromise(runPromise(Effect.provideService(effect, Scope.Scope, scope), options)) + + return () => { runPromise(Scope.close(scope, Exit.void)) } + }, [ + ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runPromise], + ...(deps ?? []), + ]) + + return React.use(promise) + } + useRef(value: A): SubscriptionRef.SubscriptionRef { return this.useMemo(