diff --git a/packages/effect-components/src/ReactComponent.ts b/packages/effect-components/src/ReactComponent.ts
index af24644..06c33e5 100644
--- a/packages/effect-components/src/ReactComponent.ts
+++ b/packages/effect-components/src/ReactComponent.ts
@@ -1,24 +1,30 @@
-import { Effect, Runtime, type Scope } from "effect"
+import { Effect, ExecutionStrategy, Exit, Ref, Runtime, Scope } from "effect"
import * as React from "react"
+import * as ReactHook from "./ReactHook.js"
export interface ReactComponent
{
(props: P): Effect.Effect
}
+
export const use = (
self: ReactComponent
,
fn: (Component: React.FC
) => React.ReactNode,
-): Effect.Effect => Effect.map(
+ options?: ReactHook.ScopeOptions,
+): Effect.Effect> => Effect.map(
Effect.runtime(),
- runtime => fn(props => Runtime.runSync(runtime)(self(props))),
+ runtime => fn(props =>
+ Runtime.runSync(runtime)(Effect.provideService(self(props), Scope.Scope, useScope(runtime, options)))
+ ),
)
export const useFC = (
- self: ReactComponent
+ self: ReactComponent
,
+ options?: ReactHook.ScopeOptions,
): Effect.Effect, never, R | Scope.Scope> => Effect.map(
Effect.runtime(),
- runtime => props => Runtime.runSync(runtime)(self(props)),
+ runtime => props => Runtime.runSync(runtime)(Effect.provideService(self(props), Scope.Scope, useScope(runtime, options))),
)
export const createElement = (
@@ -34,6 +40,45 @@ export const createElement =
(
),
)
-export const useScope: Effect.Effect = Effect.gen(function*() {
-})
+const useScope = (
+ runtime: Runtime.Runtime,
+ options?: ReactHook.ScopeOptions,
+): Scope.Scope => {
+ const [isInitialRun, initialScope] = React.useMemo(() => Runtime.runSync(runtime)(
+ Effect.all([Ref.make(true), makeScope(options)])
+ ), [])
+ const [scope, setScope] = React.useState(initialScope)
+
+ React.useEffect(() => Runtime.runSync(runtime)(
+ Effect.if(isInitialRun, {
+ onTrue: () => Effect.as(
+ Ref.set(isInitialRun, false),
+ () => closeScope(scope, runtime, options),
+ ),
+
+ onFalse: () => makeScope(options).pipe(
+ Effect.tap(scope => Effect.sync(() => setScope(scope))),
+ Effect.map(scope => () => closeScope(scope, runtime, options)),
+ ),
+ })
+ ), [])
+
+ return scope
+}
+
+const makeScope = (options?: ReactHook.ScopeOptions) => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
+const closeScope = (
+ scope: Scope.CloseableScope,
+ runtime: Runtime.Runtime,
+ options?: ReactHook.ScopeOptions,
+) => {
+ switch (options?.finalizerExecutionMode ?? "sync") {
+ case "sync":
+ Runtime.runSync(runtime)(Scope.close(scope, Exit.void))
+ break
+ case "fork":
+ Runtime.runFork(runtime)(Scope.close(scope, Exit.void))
+ break
+ }
+}
diff --git a/packages/effect-components/src/hooks.ts b/packages/effect-components/src/ReactHook.ts
similarity index 100%
rename from packages/effect-components/src/hooks.ts
rename to packages/effect-components/src/ReactHook.ts
diff --git a/packages/effect-components/src/index.ts b/packages/effect-components/src/index.ts
index 153be4b..3db5a6c 100644
--- a/packages/effect-components/src/index.ts
+++ b/packages/effect-components/src/index.ts
@@ -1,3 +1,2 @@
-export * from "./hooks.js"
export * as ReactComponent from "./ReactComponent.js"
-export { use, useFC, createElement } from "./ReactComponent.js"
+export * as ReactHook from "./ReactHook.js"