import { Effect, ExecutionStrategy, Runtime, Scope } from "effect"
import * as React from "react"
export interface ScopeOptions {
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
readonly finalizerExecutionMode?: "sync" | "fork"
}
export const useMemo: {
(
factory: () => Effect.Effect,
deps: React.DependencyList,
): Effect.Effect
} = Effect.fnUntraced(function* useMemo(
factory: () => Effect.Effect,
deps: React.DependencyList,
) {
const runtime = yield* Effect.runtime()
return React.useMemo(() => Runtime.runSync(runtime)(factory()), deps)
})
export const useOnce: {
(factory: () => Effect.Effect): Effect.Effect
} = Effect.fnUntraced(function* useOnce(
factory: () => Effect.Effect
) {
return yield* useMemo(factory, [])
})
export const useEffect: {
(
effect: () => Effect.Effect,
deps?: React.DependencyList,
options?: ScopeOptions,
): Effect.Effect
} = Effect.fnUntraced(function* useEffect(
effect: () => Effect.Effect,
deps?: React.DependencyList,
options?: ScopeOptions,
) {
const runtime = yield* Effect.runtime()
React.useEffect(() => {
const { scope, exit } = Effect.Do.pipe(
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
Runtime.runSync(runtime),
)
return () => {
switch (options?.finalizerExecutionMode ?? "sync") {
case "sync":
Runtime.runSync(runtime)(Scope.close(scope, exit))
break
case "fork":
Runtime.runFork(runtime)(Scope.close(scope, exit))
break
}
}
}, deps)
})
export const useLayoutEffect: {
(
effect: () => Effect.Effect,
deps?: React.DependencyList,
options?: ScopeOptions,
): Effect.Effect
} = Effect.fnUntraced(function* useLayoutEffect(
effect: () => Effect.Effect,
deps?: React.DependencyList,
options?: ScopeOptions,
) {
const runtime = yield* Effect.runtime()
React.useLayoutEffect(() => {
const { scope, exit } = Effect.Do.pipe(
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
Runtime.runSync(runtime),
)
return () => {
switch (options?.finalizerExecutionMode ?? "sync") {
case "sync":
Runtime.runSync(runtime)(Scope.close(scope, exit))
break
case "fork":
Runtime.runFork(runtime)(Scope.close(scope, exit))
break
}
}
}, deps)
})