From aa46ecc82dbe5eb30824917b7a6b365743131da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 28 Mar 2025 20:27:25 +0100 Subject: [PATCH] Async provider refactoring --- packages/reffuse/src/ReffuseContext.ts | 36 ++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/packages/reffuse/src/ReffuseContext.ts b/packages/reffuse/src/ReffuseContext.ts index 6096857..ce970fa 100644 --- a/packages/reffuse/src/ReffuseContext.ts +++ b/packages/reffuse/src/ReffuseContext.ts @@ -1,4 +1,4 @@ -import { Array, Context, Effect, Exit, Layer, Ref, Runtime, Scope } from "effect" +import { Array, Context, Effect, ExecutionStrategy, Exit, Layer, Ref, Runtime, Scope } from "effect" import * as React from "react" import * as ReffuseRuntime from "./ReffuseRuntime.js" @@ -76,6 +76,10 @@ const makeProvider = (Context: React.Context>): ReactProvi export type AsyncReactProvider = React.FC<{ readonly layer: Layer.Layer + readonly options?: { + readonly scope?: Scope.Scope + readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy + } readonly fallback?: React.ReactNode readonly children?: React.ReactNode }> @@ -93,11 +97,33 @@ const makeAsyncProvider = (Context: React.Context>): Async return function ReffuseContextAsyncReactProvider(props) { const runtime = ReffuseRuntime.useRuntime() + const runSync = React.useMemo(() => Runtime.runSync(runtime), [runtime]) + const runFork = React.useMemo(() => Runtime.runFork(runtime), [runtime]) - const promise = React.useMemo(() => Effect.context().pipe( - Effect.provide(props.layer), - Runtime.runPromise(runtime), - ), [props.layer, runtime]) + const [promise, setPromise] = React.useState(Promise.withResolvers>().promise) + + React.useEffect(() => { + const { promise, resolve, reject } = Promise.withResolvers>() + setPromise(promise) + + const scope = runSync(props.options?.scope + ? Scope.fork(props.options.scope, props.options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential) + : Scope.make(props.options?.finalizerExecutionStrategy) + ) + + Effect.context().pipe( + Effect.match({ + onSuccess: resolve, + onFailure: reject, + }), + + Effect.provide(props.layer), + Effect.provideService(Scope.Scope, scope), + effect => runFork(effect, { ...props.options, scope }), + ) + + return () => { runFork(Scope.close(scope, Exit.void)) } + }, [props.layer, runSync, runFork]) return React.createElement(React.Suspense, { children: React.createElement(ReffuseContextAsyncReactProviderInner, { ...props, promise }),