From 87d27dd48ddaa8d9581795489aa174f0dcf2892c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Thu, 1 May 2025 19:18:54 +0200 Subject: [PATCH] useScope work --- packages/reffuse/src/ReffuseNamespace.ts | 72 ++++++++---------------- 1 file changed, 22 insertions(+), 50 deletions(-) diff --git a/packages/reffuse/src/ReffuseNamespace.ts b/packages/reffuse/src/ReffuseNamespace.ts index 5acbb3f..54e22f4 100644 --- a/packages/reffuse/src/ReffuseNamespace.ts +++ b/packages/reffuse/src/ReffuseNamespace.ts @@ -1,4 +1,4 @@ -import { type Context, Effect, ExecutionStrategy, Exit, type Fiber, type Layer, Option, pipe, Pipeable, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" +import { type Context, Effect, ExecutionStrategy, Exit, type Fiber, type Layer, Match, Option, pipe, Pipeable, Queue, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" import * as React from "react" import * as ReffuseContext from "./ReffuseContext.js" import * as ReffuseRuntime from "./ReffuseRuntime.js" @@ -14,6 +14,10 @@ export interface ScopeOptions { readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy } +export interface UseScopeOptions extends ScopeOptions { + readonly finalizerExecutionMode?: "sync" | "fork" +} + export type RefsA[]> = { [K in keyof T]: Effect.Effect.Success } @@ -91,9 +95,10 @@ export abstract class ReffuseNamespace { useScope( this: ReffuseNamespace, deps: React.DependencyList = [], - options?: ScopeOptions, + options?: UseScopeOptions, ): Scope.Scope { const runSync = this.useRunSync() + const runFork = this.useRunFork() const [isInitialRun, initialScope] = React.useMemo(() => runSync(Effect.all([ Ref.make(true), @@ -106,17 +111,29 @@ export abstract class ReffuseNamespace { Effect.if({ onTrue: () => Effect.as( Ref.set(isInitialRun, false), - () => runSync(Scope.close(initialScope, Exit.void)), + () => Scope.close(initialScope, Exit.void).pipe( + effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe( + Match.when("sync", () => { runSync(effect) }), + Match.when("fork", () => { runFork(effect) }), + Match.exhaustive, + ) + ), ), onFalse: () => Scope.make(options?.finalizerExecutionStrategy).pipe( Effect.tap(v => Effect.sync(() => setScope(v))), - Effect.map(v => () => runSync(Scope.close(v, Exit.void))), + Effect.map(v => () => Scope.close(v, Exit.void).pipe( + effect => Match.value(options?.finalizerExecutionMode ?? "sync").pipe( + Match.when("sync", () => { runSync(effect) }), + Match.when("fork", () => { runFork(effect) }), + Match.exhaustive, + ) + )), ), }), runSync, - ), [runSync, ...deps]) + ), [runSync, runFork, ...deps]) return scope } @@ -144,51 +161,6 @@ export abstract class ReffuseNamespace { ]) } - useMemoScoped( - this: ReffuseNamespace, - effect: () => Effect.Effect, - deps: React.DependencyList, - options?: RenderOptions & ScopeOptions, - ): A { - const runSync = this.useRunSync() - - const { isInitialRun, initialScope, initialValue } = React.useMemo(() => Effect.Do.pipe( - Effect.bind("isInitialRun", () => Ref.make(true)), - Effect.bind("initialScope", () => Scope.make(options?.finalizerExecutionStrategy)), - Effect.bind("initialValue", ({ initialScope }) => Effect.provideService(effect(), Scope.Scope, initialScope)), - runSync, - ), []) - - const [value, setValue] = React.useState(initialValue) - - React.useEffect(() => isInitialRun.pipe( - Effect.if({ - onTrue: () => Effect.as( - Ref.set(isInitialRun, false), - () => runSync(Scope.close(initialScope, Exit.void)), - ), - - onFalse: () => Effect.Do.pipe( - Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy)), - Effect.bind("value", ({ scope }) => Effect.provideService(effect(), Scope.Scope, scope)), - Effect.tap(({ value }) => - Effect.sync(() => setValue(value)) - ), - Effect.map(({ scope }) => - () => runSync(Scope.close(scope, Exit.void)) - ), - ), - }), - - runSync, - ), [ - ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync], - ...deps, - ]) - - return value - } - /** * Reffuse equivalent to `React.useEffect`. *