diff --git a/packages/reffuse/src/Reffuse.tsx b/packages/reffuse/src/Reffuse.tsx index 2f6fdd0..5b4f062 100644 --- a/packages/reffuse/src/Reffuse.tsx +++ b/packages/reffuse/src/Reffuse.tsx @@ -1,5 +1,6 @@ -import { Context, Effect, Fiber, FiberRefs, Layer, ManagedRuntime, Ref, Runtime, RuntimeFlags, Scope, Stream, SubscriptionRef } from "effect" +import { Context, Effect, Fiber, Layer, ManagedRuntime, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" import React from "react" +import * as ReffuseReactContext from "./ReffuseReactContext.js" export interface ProviderProps { @@ -10,31 +11,71 @@ export interface ProviderProps { export class Reffuse { - readonly Context = React.createContext>(null!) - readonly Provider: React.FC> + readonly Context = React.createContext>(null!) + readonly Provider: React.FC> constructor( runtime: Runtime.Runtime ) { - this.Provider = (props: ProviderProps) => { - const runtime = React.useMemo(() => Runtime.make({ - context: Context.empty(), - runtimeFlags: RuntimeFlags.make(), - fiberRefs: FiberRefs.empty(), - }), []) + this.Provider = (props: ProviderProps) => { + const value = React.useMemo(() => ({ + runtime, + context: Effect.context().pipe( + Effect.provide(props.layer), + Runtime.runSync(runtime), + ), + }), [props.layer]) return ( ) } } - useRuntime(): Runtime.Runtime { - return React.useContext(this.Context) + useRuntime(): Runtime.Runtime { + return React.useContext(this.Context).runtime + } + + useContext(): Context.Context { + return React.useContext(this.Context).context + } + + + useRunSync() { + const { runtime, context } = React.useContext(this.Context) + + return React.useCallback((effect: Effect.Effect): A => effect.pipe( + Effect.provide(context), + Runtime.runSync(runtime), + ), [runtime, context]) + } + + useRunPromise() { + const { runtime, context } = React.useContext(this.Context) + + return React.useCallback(( + effect: Effect.Effect, + options?: { readonly signal?: AbortSignal }, + ): Promise => effect.pipe( + Effect.provide(context), + effect => Runtime.runPromise(runtime)(effect, options), + ), [runtime, context]) + } + + useRunFork() { + const { runtime, context } = React.useContext(this.Context) + + return React.useCallback(( + effect: Effect.Effect, + options?: Runtime.RunForkOptions, + ): Fiber.RuntimeFiber => effect.pipe( + Effect.provide(context), + effect => Runtime.runFork(runtime)(effect, options), + ), [runtime, context]) } @@ -43,32 +84,32 @@ export class Reffuse { deps?: React.DependencyList, options?: Runtime.RunForkOptions, ): void { - const runtime = this.useRuntime() + const runFork = this.useRunFork() return React.useEffect(() => { - const fiber = runtime.runFork(Effect.scoped(self), options) - return () => { runtime.runFork(Fiber.interrupt(fiber)) } - }, [runtime, ...deps ?? []]) + const fiber = runFork(Effect.scoped(self), options) + return () => { runFork(Fiber.interrupt(fiber)) } + }, [runFork, ...deps ?? []]) } useRef(value: A): SubscriptionRef.SubscriptionRef { - const runtime = this.useRuntime() - return React.useMemo(() => runtime.runSync(SubscriptionRef.make(value)), []) + const runSync = this.useRunSync() + return React.useMemo(() => runSync(SubscriptionRef.make(value)), []) } useRefFromEffect(effect: Effect.Effect): SubscriptionRef.SubscriptionRef { - const runtime = this.useRuntime() + const runSync = this.useRunSync() - return React.useMemo(() => runtime.runSync(effect.pipe( + return React.useMemo(() => runSync(effect.pipe( Effect.flatMap(SubscriptionRef.make) )), []) } useRefState(ref: SubscriptionRef.SubscriptionRef): [A, React.Dispatch>] { - const runtime = this.useRuntime() + const runSync = this.useRunSync() - const initialState = React.useMemo(() => runtime.runSync(ref), [ref]) + const initialState = React.useMemo(() => runSync(ref), [ref]) const [reactStateValue, setReactStateValue] = React.useState(initialState) this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() => @@ -76,7 +117,7 @@ export class Reffuse { )), [ref]) const setValue = React.useCallback((setStateAction: React.SetStateAction) => - runtime.runSync(Ref.update(ref, previousState => + runSync(Ref.update(ref, previousState => typeof setStateAction === "function" ? (setStateAction as (prevState: A) => A)(previousState) : setStateAction @@ -89,5 +130,5 @@ export class Reffuse { } -export const make = (layer: Layer.Layer): Reffuse => +export const make = (): Reffuse => new Reffuse(ManagedRuntime.make(layer)) diff --git a/packages/reffuse/src/ReffuseReactContext.ts b/packages/reffuse/src/ReffuseReactContext.ts new file mode 100644 index 0000000..54c6552 --- /dev/null +++ b/packages/reffuse/src/ReffuseReactContext.ts @@ -0,0 +1,7 @@ +import type { Context, Runtime } from "effect" + + +export interface ReffuseReactContext { + readonly runtime: Runtime.Runtime + readonly context: Context.Context +} diff --git a/packages/reffuse/src/ReffuseReactContextProvider.tsx b/packages/reffuse/src/ReffuseReactContextProvider.tsx new file mode 100644 index 0000000..e69de29