diff --git a/packages/effect-components/src/ReactComponent.ts b/packages/effect-components/src/ReactComponent.ts index 118b6ac..6960543 100644 --- a/packages/effect-components/src/ReactComponent.ts +++ b/packages/effect-components/src/ReactComponent.ts @@ -7,71 +7,48 @@ export interface ReactComponent { (props: P): Effect.Effect } +export const nonReactiveTags = [Tracer.ParentSpan] as const + export const useFC: { ( self: ReactComponent, options?: ReactHook.ScopeOptions, ): Effect.Effect, never, Exclude> -} = Effect.fn(function* useFC( +} = Effect.fnUntraced(function* useFC( self: ReactComponent, options?: ReactHook.ScopeOptions, ) { const runtime = yield* Effect.runtime>() - return React.useCallback((props: P) => Runtime.runSync(runtime)( - self(props) as Effect.Effect> - ), []) + return React.useCallback((props: P) => { + 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 Runtime.runSync(runtime)( + Effect.provideService(self(props), Scope.Scope, scope) + ) + }, Array.from( + Context.omit(...nonReactiveTags)(runtime.context).unsafeMap.values() + )) }) -export const use: { - ( - self: ReactComponent, - fn: (Component: React.FC

) => React.ReactNode, - options?: ReactHook.ScopeOptions, - ): Effect.Effect> -} = Effect.fn(function* use( - self: ReactComponent, - fn: (Component: React.FC

) => React.ReactNode, - options?: ReactHook.ScopeOptions, -) { - return fn(yield* useFC(self, options)) -}) - - -const FC = ( - self: ReactComponent, - runtime: Runtime.Runtime, - props: P, - options?: ReactHook.ScopeOptions, -): React.ReactNode => { - 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 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) const closeScope = ( scope: Scope.CloseableScope, @@ -87,3 +64,17 @@ const closeScope = ( break } } + +export const use: { + ( + self: ReactComponent, + fn: (Component: React.FC

) => React.ReactNode, + options?: ReactHook.ScopeOptions, + ): Effect.Effect> +} = Effect.fnUntraced(function* use( + self: ReactComponent, + fn: (Component: React.FC

) => React.ReactNode, + options?: ReactHook.ScopeOptions, +) { + return fn(yield* useFC(self, options)) +}) diff --git a/packages/example/src/routes/effect-component-tests.tsx b/packages/example/src/routes/effect-component-tests.tsx index 9284220..d95daa1 100644 --- a/packages/example/src/routes/effect-component-tests.tsx +++ b/packages/example/src/routes/effect-component-tests.tsx @@ -15,9 +15,7 @@ function RouteComponent() { return <> {runtime.runSync(ReactComponent.use(MyTestComponent, Component => ( - )).pipe( - Effect.scoped - ))} + )))} } @@ -30,10 +28,10 @@ const MyTestComponent = Effect.fn(function* MyTestComponent(props?: { readonly v const [state, setState] = React.useState("value") const effectValue = yield* Effect.succeed(`state: ${ state }`) - yield* ReactHook.useOnce(() => Effect.andThen( + yield* ReactHook.useEffect(() => Effect.andThen( Effect.addFinalizer(() => Console.log("MyTestComponent umounted")), Console.log("MyTestComponent mounted"), - )) + ), []) return <> {effectValue}