diff --git a/packages/effect-components/src/ReactComponent.ts b/packages/effect-components/src/ReactComponent.ts index a7bf827..c321fb7 100644 --- a/packages/effect-components/src/ReactComponent.ts +++ b/packages/effect-components/src/ReactComponent.ts @@ -1,5 +1,5 @@ import { Effect, Runtime, type Scope } from "effect" -import type * as React from "react" +import * as React from "react" export interface ReactComponent { @@ -8,8 +8,28 @@ export interface ReactComponent { export const use = ( self: ReactComponent, - fn: (Component: React.FC

) => React.ReactNode + fn: (Component: React.FC

) => React.ReactNode, ): Effect.Effect => Effect.map( Effect.runtime(), runtime => fn(props => Runtime.runSync(runtime)(self(props))), ) + +export const useFC = ( + self: ReactComponent +): Effect.Effect, never, R | Scope.Scope> => Effect.map( + Effect.runtime(), + runtime => props => Runtime.runSync(runtime)(self(props)), +) + +export const createElement = ( + 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, + ), +) diff --git a/packages/effect-components/src/hooks.ts b/packages/effect-components/src/hooks.ts new file mode 100644 index 0000000..4179ba0 --- /dev/null +++ b/packages/effect-components/src/hooks.ts @@ -0,0 +1,49 @@ +import { Effect, type ExecutionStrategy, Runtime, Scope } from "effect" +import * as React from "react" + + +export interface ScopeOptions { + readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy + readonly finalizerExecutionMode?: "sync" | "fork" +} + + +export const useScope: Effect.Effect = Effect.gen(function*() { + +}) + +export const useEffect = ( + effect: () => Effect.Effect, + deps?: React.DependencyList, + options?: ScopeOptions, +): Effect.Effect => Effect.gen(function*() { + const runtime = yield* Effect.runtime() + + React.useEffect(() => { + const { scope, exit } = Effect.Do.pipe( + Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy)), + Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))), + Runtime.runSync(runtime), + ) + + return () => { Runtime.runSync(runtime)(Scope.close(scope, exit)) } + }, deps) +}) + +export const useLayoutEffect = ( + effect: () => Effect.Effect, + deps?: React.DependencyList, + options?: ScopeOptions, +): Effect.Effect => Effect.gen(function*() { + const runtime = yield* Effect.runtime() + + React.useLayoutEffect(() => { + const { scope, exit } = Effect.Do.pipe( + Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy)), + Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))), + Runtime.runSync(runtime), + ) + + return () => { Runtime.runSync(runtime)(Scope.close(scope, exit)) } + }, deps) +}) diff --git a/packages/effect-components/src/index.ts b/packages/effect-components/src/index.ts index 17a9fe1..153be4b 100644 --- a/packages/effect-components/src/index.ts +++ b/packages/effect-components/src/index.ts @@ -1,2 +1,3 @@ +export * from "./hooks.js" export * as ReactComponent from "./ReactComponent.js" -export { use } from "./ReactComponent.js" +export { use, useFC, createElement } from "./ReactComponent.js"