From a5eb50eec9243a326ba07e28318138d79a80a6e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 5 Jul 2025 00:20:37 +0200 Subject: [PATCH] Refactoring --- packages/effect-fc/src/ReactComponent.ts | 51 ++++++++++++++++++++++-- packages/effect-fc/src/ReactHook.ts | 44 -------------------- 2 files changed, 48 insertions(+), 47 deletions(-) diff --git a/packages/effect-fc/src/ReactComponent.ts b/packages/effect-fc/src/ReactComponent.ts index aacec01..9b4d85c 100644 --- a/packages/effect-fc/src/ReactComponent.ts +++ b/packages/effect-fc/src/ReactComponent.ts @@ -1,7 +1,7 @@ -import { Context, Effect, Function, Runtime, Scope, Tracer } from "effect" +import { Context, Effect, ExecutionStrategy, Exit, Function, Ref, Runtime, Scope, Tracer } from "effect" import type { Mutable } from "effect/Types" import * as React from "react" -import * as ReactHook from "./ReactHook.js" +import type * as ReactHook from "./ReactHook.js" export interface ReactComponent { @@ -11,6 +11,7 @@ export interface ReactComponent { export const nonReactiveTags = [Tracer.ParentSpan] as const + export const withDisplayName: { >(displayName: string): (self: C) => C >(self: C, displayName: string): C @@ -22,6 +23,7 @@ export const withDisplayName: { return self }) + export const useFC: { ( self: ReactComponent, @@ -35,7 +37,7 @@ export const useFC: { runtimeRef.current = yield* Effect.runtime>() return React.useMemo(() => function ScopeProvider(props: P) { - const scope = Runtime.runSync(runtimeRef.current)(ReactHook.useScope(options)) + const scope = useScope(runtimeRef.current, options) const FC = React.useMemo(() => { const f = (props: P) => Runtime.runSync(runtimeRef.current)( @@ -51,6 +53,49 @@ export const useFC: { )) }) +const useScope = ( + runtime: Runtime.Runtime, + options?: ReactHook.ScopeOptions, +) => { + 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 + } +} + + export const use: { ( self: ReactComponent, diff --git a/packages/effect-fc/src/ReactHook.ts b/packages/effect-fc/src/ReactHook.ts index 1eb7041..141408f 100644 --- a/packages/effect-fc/src/ReactHook.ts +++ b/packages/effect-fc/src/ReactHook.ts @@ -9,50 +9,6 @@ export interface ScopeOptions { } -export const useScope: { - (options?: ScopeOptions): Effect.Effect -} = Effect.fnUntraced(function* (options?: ScopeOptions) { - const runtime = yield* Effect.runtime() - - 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?: ScopeOptions) => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential) -const closeScope = ( - scope: Scope.CloseableScope, - runtime: Runtime.Runtime, - options?: 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 - } -} - - export const useMemo: { ( factory: () => Effect.Effect,