diff --git a/packages/effect-components/src/ReactComponent.ts b/packages/effect-components/src/ReactComponent.ts index d205fd7..211bbe0 100644 --- a/packages/effect-components/src/ReactComponent.ts +++ b/packages/effect-components/src/ReactComponent.ts @@ -1,4 +1,4 @@ -import { Effect, ExecutionStrategy, Exit, Ref, Runtime, Scope } from "effect" +import { Context, Effect, ExecutionStrategy, Exit, Ref, Runtime, Scope, Tracer } from "effect" import * as React from "react" import * as ReactHook from "./ReactHook.js" @@ -14,9 +14,7 @@ export const use = ( options?: ReactHook.ScopeOptions, ): Effect.Effect> => Effect.map( Effect.runtime(), - runtime => fn(props => - Runtime.runSync(runtime)(Effect.provideService(self(props), Scope.Scope, useScope(runtime, options))) - ), + runtime => fn(props => FC(self, runtime, props, options)), ) export const useFC = ( @@ -24,27 +22,16 @@ export const useFC = ( options?: ReactHook.ScopeOptions, ): Effect.Effect, never, Exclude> => Effect.map( Effect.runtime(), - runtime => props => Runtime.runSync(runtime)(Effect.provideService(self(props), Scope.Scope, useScope(runtime, options))), + runtime => props => FC(self, runtime, props, options), ) -export const createElement = ( + +const FC = ( self: ReactComponent, - props?: React.Attributes & P | null, - ...children: React.ReactNode[] -): Effect.Effect => Effect.map( - Effect.runtime(), - runtime => React.createElement( - props => Runtime.runSync(runtime)(self(props)), - props, - ...children, - ), -) - - -const useScope = ( - runtime: Runtime.Runtime, + runtime: Runtime.Runtime, + props: P, options?: ReactHook.ScopeOptions, -): Scope.Scope => { +): React.ReactNode => { const [isInitialRun, initialScope] = React.useMemo(() => Runtime.runSync(runtime)( Effect.all([Ref.make(true), makeScope(options)]) ), []) @@ -64,7 +51,12 @@ const useScope = ( }) ), []) - return scope + return React.useMemo(() => Runtime.runSync(runtime)( + Effect.provideService(self(props), Scope.Scope, scope) + ), [ + props, + ...Array.from(Context.omit(Tracer.ParentSpan)(runtime.context).unsafeMap.values()), + ]) } const makeScope = (options?: ReactHook.ScopeOptions) => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential) diff --git a/packages/example/src/routes/effect-component-tests.tsx b/packages/example/src/routes/effect-component-tests.tsx index cc87d70..9284220 100644 --- a/packages/example/src/routes/effect-component-tests.tsx +++ b/packages/example/src/routes/effect-component-tests.tsx @@ -1,6 +1,6 @@ import { Box, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" -import { Console, Effect, Layer, ManagedRuntime } from "effect" +import { Console, Effect, Layer, ManagedRuntime, SubscriptionRef } from "effect" import { ReactComponent, ReactHook } from "effect-components" import * as React from "react" @@ -22,6 +22,10 @@ function RouteComponent() { } +class TestService extends Effect.Service()("TestService", { + effect: Effect.bind(Effect.Do, "ref", () => SubscriptionRef.make("value")), +}) {} + const MyTestComponent = Effect.fn(function* MyTestComponent(props?: { readonly value?: string }) { const [state, setState] = React.useState("value") const effectValue = yield* Effect.succeed(`state: ${ state }`)