diff --git a/packages/effect-fc/src/Hooks/ScopeOptions.ts b/packages/effect-fc/src/Hooks/ScopeOptions.ts deleted file mode 100644 index c2654e8..0000000 --- a/packages/effect-fc/src/Hooks/ScopeOptions.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { ExecutionStrategy } from "effect" - - -export interface ScopeOptions { - readonly finalizerExecutionMode?: "sync" | "fork" - readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy -} diff --git a/packages/effect-fc/src/Hooks/index.ts b/packages/effect-fc/src/Hooks/index.ts deleted file mode 100644 index 0f99a97..0000000 --- a/packages/effect-fc/src/Hooks/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -export * from "./input/index.js" -export * from "./ScopeOptions.js" -export * from "./useCallbackPromise.js" -export * from "./useCallbackSync.js" -export * from "./useContext.js" -export * from "./useEffect.js" -export * from "./useFork.js" -export * from "./useLayoutEffect.js" -export * from "./useMemo.js" -export * from "./useOnce.js" -export * from "./useRefFromState.js" -export * from "./useRefState.js" -export * from "./useScope.js" -export * from "./useStreamFromReactiveValues.js" -export * from "./useSubscribables.js" -export * from "./useSubscribeStream.js" diff --git a/packages/effect-fc/src/Hooks/input/index.ts b/packages/effect-fc/src/Hooks/input/index.ts deleted file mode 100644 index bc1c4dc..0000000 --- a/packages/effect-fc/src/Hooks/input/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./useInput.js" -export * from "./useOptionalInput.js" diff --git a/packages/effect-fc/src/Hooks/input/useInput.ts b/packages/effect-fc/src/Hooks/input/useInput.ts deleted file mode 100644 index 215eac6..0000000 --- a/packages/effect-fc/src/Hooks/input/useInput.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { type Duration, Effect, Equal, Equivalence, flow, identity, Option, type ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect" -import * as React from "react" -import { useFork } from "../useFork.js" -import { useOnce } from "../useOnce.js" -import { useRefState } from "../useRefState.js" - - -export namespace useInput { - export interface Options { - readonly schema: Schema.Schema - readonly equivalence?: Equivalence.Equivalence - readonly ref: SubscriptionRef.SubscriptionRef - readonly debounce?: Duration.DurationInput - } - - export interface Result { - readonly value: string - readonly setValue: React.Dispatch> - readonly error: Option.Option - } -} - -export const useInput: { - (options: useInput.Options): Effect.Effect -} = Effect.fnUntraced(function* (options: useInput.Options) { - const internalRef = yield* useOnce(() => options.ref.pipe( - Effect.andThen(Schema.encode(options.schema)), - Effect.andThen(SubscriptionRef.make), - )) - const [error, setError] = React.useState(Option.none()) - - yield* useFork(() => Effect.all([ - // Sync the upstream state with the internal state - // Only mutate the internal state if the upstream value is actually different. This avoids infinite re-render loops. - Stream.runForEach(Stream.changesWith(options.ref.changes, Equivalence.strict()), upstreamValue => - Effect.whenEffect( - Effect.andThen( - Schema.encode(options.schema)(upstreamValue), - encodedUpstreamValue => Ref.set(internalRef, encodedUpstreamValue), - ), - internalRef.pipe( - Effect.andThen(Schema.decode(options.schema)), - Effect.andThen(decodedInternalValue => !(options.equivalence ?? Equal.equals)(upstreamValue, decodedInternalValue)), - Effect.catchTag("ParseError", () => Effect.succeed(false)), - ), - ) - ), - - // Sync all changes to the internal state with upstream - Stream.runForEach( - internalRef.changes.pipe( - Stream.changesWith(Equivalence.strict()), - options.debounce ? Stream.debounce(options.debounce) : identity, - Stream.drop(1), - ), - flow( - Schema.decode(options.schema), - Effect.andThen(v => Ref.set(options.ref, v)), - Effect.andThen(() => setError(Option.none())), - Effect.catchTag("ParseError", e => Effect.sync(() => setError(Option.some(e)))), - ), - ), - ], { concurrency: "unbounded" }), [options.schema, options.equivalence, options.ref, options.debounce, internalRef]) - - const [value, setValue] = yield* useRefState(internalRef) - return { value, setValue, error } -}) diff --git a/packages/effect-fc/src/Hooks/input/useOptionalInput.ts b/packages/effect-fc/src/Hooks/input/useOptionalInput.ts deleted file mode 100644 index 0f519aa..0000000 --- a/packages/effect-fc/src/Hooks/input/useOptionalInput.ts +++ /dev/null @@ -1,107 +0,0 @@ -import { type Duration, Effect, Equal, Equivalence, flow, identity, Option, type ParseResult, Ref, Schema, Stream, SubscriptionRef } from "effect" -import * as React from "react" -import * as SetStateAction from "../../SetStateAction.js" -import { useCallbackSync } from "../useCallbackSync.js" -import { useFork } from "../useFork.js" -import { useOnce } from "../useOnce.js" -import { useRefState } from "../useRefState.js" -import { useSubscribables } from "../useSubscribables.js" - - -export namespace useOptionalInput { - export interface Options { - readonly schema: Schema.Schema - readonly defaultValue?: A - readonly equivalence?: Equivalence.Equivalence - readonly ref: SubscriptionRef.SubscriptionRef> - readonly debounce?: Duration.DurationInput - } - - export interface Result { - readonly value: string - readonly setValue: React.Dispatch> - readonly enabled: boolean - readonly setEnabled: React.Dispatch> - readonly error: Option.Option - } -} - -export const useOptionalInput: { - (options: useOptionalInput.Options): Effect.Effect -} = Effect.fnUntraced(function* (options: useOptionalInput.Options) { - const [internalRef, enabledRef] = yield* useOnce(() => Effect.andThen(options.ref, upstreamValue => - Effect.all([ - Effect.andThen( - Option.match(upstreamValue, { - onSome: Schema.encode(options.schema), - onNone: () => options.defaultValue - ? Schema.encode(options.schema)(options.defaultValue) - : Effect.succeed(""), - }), - SubscriptionRef.make, - ), - - SubscriptionRef.make(Option.isSome(upstreamValue)), - ]) - )) - - const [error, setError] = React.useState(Option.none()) - - yield* useFork(() => Effect.all([ - // Sync the upstream state with the internal state - // Only mutate the internal state if the upstream value is actually different. This avoids infinite re-render loops. - Stream.runForEach(Stream.changesWith(options.ref.changes, Equivalence.strict()), Option.match({ - onSome: upstreamValue => Effect.andThen( - Ref.set(enabledRef, true), - - Effect.whenEffect( - Effect.andThen( - Schema.encode(options.schema)(upstreamValue), - encodedUpstreamValue => Ref.set(internalRef, encodedUpstreamValue), - ), - internalRef.pipe( - Effect.andThen(Schema.decode(options.schema)), - Effect.andThen(decodedInternalValue => !(options.equivalence ?? Equal.equals)(upstreamValue, decodedInternalValue)), - Effect.catchTag("ParseError", () => Effect.succeed(false)), - ), - ), - ), - - onNone: () => Ref.set(enabledRef, false), - })), - - // Sync all changes to the internal state with upstream - Stream.runForEach( - internalRef.changes.pipe( - Stream.changesWith(Equivalence.strict()), - options.debounce ? Stream.debounce(options.debounce) : identity, - Stream.drop(1), - ), - flow( - Schema.decode(options.schema), - Effect.andThen(v => Ref.set(options.ref, Option.some(v))), - Effect.andThen(() => setError(Option.none())), - Effect.catchTag("ParseError", e => Effect.sync(() => setError(Option.some(e)))), - ), - ), - ], { concurrency: "unbounded" }), [options.schema, options.equivalence, options.ref, options.debounce, internalRef]) - - const setEnabled = yield* useCallbackSync( - (setStateAction: React.SetStateAction) => Effect.andThen( - Ref.updateAndGet(enabledRef, prevState => SetStateAction.value(setStateAction, prevState)), - enabled => enabled - ? internalRef.pipe( - Effect.andThen(Schema.decode(options.schema)), - Effect.andThen(v => Ref.set(options.ref, Option.some(v))), - Effect.andThen(() => setError(Option.none())), - Effect.catchTag("ParseError", e => Effect.sync(() => setError(Option.some(e)))), - ) - : Ref.set(options.ref, Option.none()), - ), - [options.schema, options.ref, internalRef, enabledRef], - ) - - const [enabled] = yield* useSubscribables(enabledRef) - const [value, setValue] = yield* useRefState(internalRef) - return { value, setValue, enabled, setEnabled, error } -}) diff --git a/packages/effect-fc/src/Hooks/internal.ts b/packages/effect-fc/src/Hooks/internal.ts deleted file mode 100644 index 4c9a35e..0000000 --- a/packages/effect-fc/src/Hooks/internal.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Exit, Runtime, Scope } from "effect" -import type { ScopeOptions } from "./ScopeOptions.js" - - -export const closeScope = ( - scope: Scope.CloseableScope, - runtime: Runtime.Runtime, - options?: ScopeOptions, -) => { - switch (options?.finalizerExecutionMode ?? "sync") { - case "sync": - Runtime.runSync(runtime)(Scope.close(scope, Exit.void)) - break - case "fork": - Runtime.runFork(runtime)(Scope.close(scope, Exit.void)) - break - } -} diff --git a/packages/effect-fc/src/Hooks/useCallbackPromise.ts b/packages/effect-fc/src/Hooks/useCallbackPromise.ts deleted file mode 100644 index 0e2ce10..0000000 --- a/packages/effect-fc/src/Hooks/useCallbackPromise.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Effect, Runtime } from "effect" -import * as React from "react" - - -export const useCallbackPromise: { - ( - callback: (...args: Args) => Effect.Effect, - deps: React.DependencyList, - ): Effect.Effect<(...args: Args) => Promise, never, R> -} = Effect.fnUntraced(function* ( - callback: (...args: Args) => Effect.Effect, - deps: React.DependencyList, -) { - // biome-ignore lint/style/noNonNullAssertion: context initialization - const runtimeRef = React.useRef>(null!) - runtimeRef.current = yield* Effect.runtime() - - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - return React.useCallback((...args: Args) => Runtime.runPromise(runtimeRef.current)(callback(...args)), deps) -}) diff --git a/packages/effect-fc/src/Hooks/useCallbackSync.ts b/packages/effect-fc/src/Hooks/useCallbackSync.ts deleted file mode 100644 index 3f96641..0000000 --- a/packages/effect-fc/src/Hooks/useCallbackSync.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Effect, Runtime } from "effect" -import * as React from "react" - - -export const useCallbackSync: { - ( - callback: (...args: Args) => Effect.Effect, - deps: React.DependencyList, - ): Effect.Effect<(...args: Args) => A, never, R> -} = Effect.fnUntraced(function* ( - callback: (...args: Args) => Effect.Effect, - deps: React.DependencyList, -) { - // biome-ignore lint/style/noNonNullAssertion: context initialization - const runtimeRef = React.useRef>(null!) - runtimeRef.current = yield* Effect.runtime() - - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - return React.useCallback((...args: Args) => Runtime.runSync(runtimeRef.current)(callback(...args)), deps) -}) diff --git a/packages/effect-fc/src/Hooks/useContext.ts b/packages/effect-fc/src/Hooks/useContext.ts deleted file mode 100644 index 16516de..0000000 --- a/packages/effect-fc/src/Hooks/useContext.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { type Context, Effect, Layer, ManagedRuntime, Scope } from "effect" -import type { ScopeOptions } from "./ScopeOptions.js" -import { useMemo } from "./useMemo.js" -import { useScope } from "./useScope.js" - - -export const useContext: { - ( - layer: Layer.Layer, - options?: ScopeOptions, - ): Effect.Effect, E, RIn> -} = Effect.fnUntraced(function* ( - layer: Layer.Layer, - options?: ScopeOptions, -) { - const scope = yield* useScope([layer], options) - - return yield* useMemo(() => Effect.context().pipe( - Effect.map(context => ManagedRuntime.make(Layer.provide(layer, Layer.succeedContext(context)))), - Effect.tap(runtime => Effect.addFinalizer(() => runtime.disposeEffect)), - Effect.andThen(runtime => runtime.runtimeEffect), - Effect.andThen(runtime => runtime.context), - Effect.provideService(Scope.Scope, scope), - ), [scope]) -}) diff --git a/packages/effect-fc/src/Hooks/useEffect.ts b/packages/effect-fc/src/Hooks/useEffect.ts deleted file mode 100644 index b325479..0000000 --- a/packages/effect-fc/src/Hooks/useEffect.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Effect, ExecutionStrategy, Runtime, Scope } from "effect" -import * as React from "react" -import { closeScope } from "./internal.js" -import type { ScopeOptions } from "./ScopeOptions.js" - - -export const useEffect: { - ( - effect: () => Effect.Effect, - deps?: React.DependencyList, - options?: ScopeOptions, - ): Effect.Effect> -} = Effect.fnUntraced(function* ( - effect: () => Effect.Effect, - deps?: React.DependencyList, - options?: ScopeOptions, -) { - const runtime = yield* Effect.runtime>() - - React.useEffect(() => Effect.Do.pipe( - Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)), - Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))), - Effect.map(({ scope }) => - () => closeScope(scope, runtime, options) - ), - Runtime.runSync(runtime), - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - ), deps) -}) diff --git a/packages/effect-fc/src/Hooks/useFork.ts b/packages/effect-fc/src/Hooks/useFork.ts deleted file mode 100644 index 0ce8a5c..0000000 --- a/packages/effect-fc/src/Hooks/useFork.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Effect, ExecutionStrategy, Runtime, Scope } from "effect" -import * as React from "react" -import { closeScope } from "./internal.js" -import type { ScopeOptions } from "./ScopeOptions.js" - - -export const useFork: { - ( - effect: () => Effect.Effect, - deps?: React.DependencyList, - options?: Runtime.RunForkOptions & ScopeOptions, - ): Effect.Effect> -} = Effect.fnUntraced(function* ( - effect: () => Effect.Effect, - deps?: React.DependencyList, - options?: Runtime.RunForkOptions & ScopeOptions, -) { - const runtime = yield* Effect.runtime>() - - React.useEffect(() => { - const scope = Runtime.runSync(runtime)(options?.scope - ? Scope.fork(options.scope, options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential) - : Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential) - ) - Runtime.runFork(runtime)(Effect.provideService(effect(), Scope.Scope, scope), { ...options, scope }) - return () => closeScope(scope, runtime, { - ...options, - finalizerExecutionMode: options?.finalizerExecutionMode ?? "fork", - }) - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - }, deps) -}) diff --git a/packages/effect-fc/src/Hooks/useLayoutEffect.ts b/packages/effect-fc/src/Hooks/useLayoutEffect.ts deleted file mode 100644 index 6f8c438..0000000 --- a/packages/effect-fc/src/Hooks/useLayoutEffect.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Effect, ExecutionStrategy, Runtime, Scope } from "effect" -import * as React from "react" -import { closeScope } from "./internal.js" -import type { ScopeOptions } from "./ScopeOptions.js" - - -export const useLayoutEffect: { - ( - effect: () => Effect.Effect, - deps?: React.DependencyList, - options?: ScopeOptions, - ): Effect.Effect> -} = Effect.fnUntraced(function* ( - effect: () => Effect.Effect, - deps?: React.DependencyList, - options?: ScopeOptions, -) { - const runtime = yield* Effect.runtime>() - - React.useLayoutEffect(() => Effect.Do.pipe( - Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)), - Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))), - Effect.map(({ scope }) => - () => closeScope(scope, runtime, options) - ), - Runtime.runSync(runtime), - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - ), deps) -}) diff --git a/packages/effect-fc/src/Hooks/useMemo.ts b/packages/effect-fc/src/Hooks/useMemo.ts deleted file mode 100644 index 43d4449..0000000 --- a/packages/effect-fc/src/Hooks/useMemo.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Effect, Runtime } from "effect" -import * as React from "react" - - -export const useMemo: { - ( - factory: () => Effect.Effect, - deps: React.DependencyList, - ): Effect.Effect -} = Effect.fnUntraced(function* ( - factory: () => Effect.Effect, - deps: React.DependencyList, -) { - const runtime = yield* Effect.runtime() - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - return yield* React.useMemo(() => Runtime.runSync(runtime)(Effect.cached(factory())), deps) -}) diff --git a/packages/effect-fc/src/Hooks/useOnce.ts b/packages/effect-fc/src/Hooks/useOnce.ts deleted file mode 100644 index f617611..0000000 --- a/packages/effect-fc/src/Hooks/useOnce.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Effect } from "effect" -import { useMemo } from "./useMemo.js" - - -export const useOnce: { - (factory: () => Effect.Effect): Effect.Effect -} = Effect.fnUntraced(function* ( - factory: () => Effect.Effect -) { - return yield* useMemo(factory, []) -}) diff --git a/packages/effect-fc/src/Hooks/useRefFromState.ts b/packages/effect-fc/src/Hooks/useRefFromState.ts deleted file mode 100644 index a6138f8..0000000 --- a/packages/effect-fc/src/Hooks/useRefFromState.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Effect, Equivalence, Ref, Stream, SubscriptionRef } from "effect" -import type * as React from "react" -import { useEffect } from "./useEffect.js" -import { useFork } from "./useFork.js" -import { useOnce } from "./useOnce.js" - - -export const useRefFromState: { - (state: readonly [A, React.Dispatch>]): Effect.Effect> -} = Effect.fnUntraced(function*([value, setValue]) { - const ref = yield* useOnce(() => SubscriptionRef.make(value)) - - yield* useEffect(() => Ref.set(ref, value), [value]) - yield* useFork(() => Stream.runForEach( - Stream.changesWith(ref.changes, Equivalence.strict()), - v => Effect.sync(() => setValue(v)), - ), [setValue]) - - return ref -}) diff --git a/packages/effect-fc/src/Hooks/useRefState.ts b/packages/effect-fc/src/Hooks/useRefState.ts deleted file mode 100644 index 9e55fbf..0000000 --- a/packages/effect-fc/src/Hooks/useRefState.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Effect, Equivalence, Ref, Stream, type SubscriptionRef } from "effect" -import * as React from "react" -import * as SetStateAction from "../SetStateAction.js" -import { useCallbackSync } from "./useCallbackSync.js" -import { useFork } from "./useFork.js" -import { useOnce } from "./useOnce.js" - - -export const useRefState: { - ( - ref: SubscriptionRef.SubscriptionRef - ): Effect.Effect>]> -} = Effect.fnUntraced(function* (ref: SubscriptionRef.SubscriptionRef) { - const [reactStateValue, setReactStateValue] = React.useState(yield* useOnce(() => ref)) - - yield* useFork(() => Stream.runForEach( - Stream.changesWith(ref.changes, Equivalence.strict()), - v => Effect.sync(() => setReactStateValue(v)), - ), [ref]) - - const setValue = yield* useCallbackSync((setStateAction: React.SetStateAction) => - Effect.andThen( - Ref.updateAndGet(ref, prevState => SetStateAction.value(setStateAction, prevState)), - v => setReactStateValue(v), - ), - [ref]) - - return [reactStateValue, setValue] -}) diff --git a/packages/effect-fc/src/Hooks/useScope.ts b/packages/effect-fc/src/Hooks/useScope.ts deleted file mode 100644 index 93212a0..0000000 --- a/packages/effect-fc/src/Hooks/useScope.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { Effect, ExecutionStrategy, Ref, Runtime, Scope } from "effect" -import * as React from "react" -import { closeScope } from "./internal.js" -import type { ScopeOptions } from "./ScopeOptions.js" - - -export const useScope: { - ( - deps: React.DependencyList, - options?: ScopeOptions, - ): Effect.Effect -} = Effect.fnUntraced(function*(deps, options) { - const runtime = yield* Effect.runtime() - - // biome-ignore lint/correctness/useExhaustiveDependencies: no reactivity needed - const [isInitialRun, initialScope] = React.useMemo(() => Runtime.runSync(runtime)(Effect.all([ - Ref.make(true), - Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential), - ])), []) - 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: () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential).pipe( - Effect.tap(scope => Effect.sync(() => setScope(scope))), - Effect.map(scope => () => closeScope(scope, runtime, options)), - ), - }) - // biome-ignore lint/correctness/useExhaustiveDependencies: use of React.DependencyList - ), deps) - - return scope -}) diff --git a/packages/effect-fc/src/Hooks/useStreamFromReactiveValues.ts b/packages/effect-fc/src/Hooks/useStreamFromReactiveValues.ts deleted file mode 100644 index a05ea28..0000000 --- a/packages/effect-fc/src/Hooks/useStreamFromReactiveValues.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Effect, PubSub, Ref, type Scope, Stream } from "effect" -import type * as React from "react" -import { useEffect } from "./useEffect.js" -import { useOnce } from "./useOnce.js" - - -export const useStreamFromReactiveValues: { - ( - values: A - ): Effect.Effect, never, Scope.Scope> -} = Effect.fnUntraced(function* (values: A) { - const { latest, pubsub, stream } = yield* useOnce(() => Effect.Do.pipe( - Effect.bind("latest", () => Ref.make(values)), - Effect.bind("pubsub", () => Effect.acquireRelease(PubSub.unbounded(), PubSub.shutdown)), - Effect.let("stream", ({ latest, pubsub }) => latest.pipe( - Effect.flatMap(a => Effect.map( - Stream.fromPubSub(pubsub, { scoped: true }), - s => Stream.concat(Stream.make(a), s), - )), - Stream.unwrapScoped, - )), - )) - - yield* useEffect(() => Ref.set(latest, values).pipe( - Effect.andThen(PubSub.publish(pubsub, values)), - Effect.unlessEffect(PubSub.isShutdown(pubsub)), - ), values) - - return stream -}) diff --git a/packages/effect-fc/src/Hooks/useSubscribables.ts b/packages/effect-fc/src/Hooks/useSubscribables.ts deleted file mode 100644 index 9d96fee..0000000 --- a/packages/effect-fc/src/Hooks/useSubscribables.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { Effect, Equivalence, pipe, Stream, type Subscribable } from "effect" -import * as React from "react" -import { useFork } from "./useFork.js" -import { useOnce } from "./useOnce.js" - - -export const useSubscribables: { - []>( - ...elements: T - ): Effect.Effect< - { [K in keyof T]: Effect.Effect.Success | Stream.Stream.Success }, - Effect.Effect.Error | Stream.Stream.Error, - Effect.Effect.Context | Stream.Stream.Context - > -} = Effect.fnUntraced(function* []>( - ...elements: T -) { - const [reactStateValue, setReactStateValue] = React.useState(yield* useOnce(() => - Effect.all(elements.map(v => v.get)) - )) - - yield* useFork(() => pipe( - elements.map(ref => Stream.changesWith(ref.changes, Equivalence.strict())), - streams => Stream.zipLatestAll(...streams), - Stream.runForEach(v => - Effect.sync(() => setReactStateValue(v)) - ), - ), elements) - - return reactStateValue as any -}) diff --git a/packages/effect-fc/src/Hooks/useSubscribeStream.ts b/packages/effect-fc/src/Hooks/useSubscribeStream.ts deleted file mode 100644 index 252054f..0000000 --- a/packages/effect-fc/src/Hooks/useSubscribeStream.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Effect, Equivalence, Option, Stream } from "effect" -import * as React from "react" -import { useFork } from "./useFork.js" - - -export const useSubscribeStream: { - ( - stream: Stream.Stream - ): Effect.Effect, never, R> - , E, R>( - stream: Stream.Stream, - initialValue: A, - ): Effect.Effect, never, R> -} = Effect.fnUntraced(function* , E, R>( - stream: Stream.Stream, - initialValue?: A, -) { - const [reactStateValue, setReactStateValue] = React.useState( - // biome-ignore lint/correctness/useExhaustiveDependencies: no reactivity needed - React.useMemo(() => initialValue - ? Option.some(initialValue) - : Option.none(), - []) - ) - - yield* useFork(() => Stream.runForEach( - Stream.changesWith(stream, Equivalence.strict()), - v => Effect.sync(() => setReactStateValue(Option.some(v))), - ), [stream]) - - return reactStateValue as Option.Some -}) diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 0164310..f63afb4 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -1,7 +1,6 @@ export * as Async from "./Async.js" export * as Component from "./Component.js" export * as Form from "./Form.js" -export * as Hooks from "./Hooks/index.js" export * as Memoized from "./Memoized.js" export * as PropertyPath from "./PropertyPath.js" export * as ReactRuntime from "./ReactRuntime.js"