0.1.0 #1

Merged
Thilawyn merged 87 commits from next into master 2025-01-18 00:54:42 +01:00
3 changed files with 72 additions and 24 deletions
Showing only changes of commit ed85f9804c - Show all commits

View File

@@ -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<R, E> {
@@ -10,31 +11,71 @@ export interface ProviderProps<R, E> {
export class Reffuse<R> {
readonly Context = React.createContext<Runtime.Runtime<R>>(null!)
readonly Provider: React.FC<ProviderProps<R, any>>
readonly Context = React.createContext<ReffuseReactContext.ReffuseReactContext<R>>(null!)
readonly Provider: React.FC<ProviderProps<R, unknown>>
constructor(
runtime: Runtime.Runtime<R>
) {
this.Provider = (props: ProviderProps<R, any>) => {
const runtime = React.useMemo(() => Runtime.make({
context: Context.empty(),
runtimeFlags: RuntimeFlags.make(),
fiberRefs: FiberRefs.empty(),
}), [])
this.Provider = (props: ProviderProps<R, unknown>) => {
const value = React.useMemo(() => ({
runtime,
context: Effect.context<R>().pipe(
Effect.provide(props.layer),
Runtime.runSync(runtime),
),
}), [props.layer])
return (
<this.Context
{...props}
value={runtime}
value={value}
/>
)
}
}
useRuntime(): Runtime.Runtime<R> {
return React.useContext(this.Context)
useRuntime(): Runtime.Runtime<never> {
return React.useContext(this.Context).runtime
}
useContext(): Context.Context<R> {
return React.useContext(this.Context).context
}
useRunSync() {
const { runtime, context } = React.useContext(this.Context)
return React.useCallback(<A, E>(effect: Effect.Effect<A, E, R>): A => effect.pipe(
Effect.provide(context),
Runtime.runSync(runtime),
), [runtime, context])
}
useRunPromise() {
const { runtime, context } = React.useContext(this.Context)
return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal },
): Promise<A> => effect.pipe(
Effect.provide(context),
effect => Runtime.runPromise(runtime)(effect, options),
), [runtime, context])
}
useRunFork() {
const { runtime, context } = React.useContext(this.Context)
return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions,
): Fiber.RuntimeFiber<A, E> => effect.pipe(
Effect.provide(context),
effect => Runtime.runFork(runtime)(effect, options),
), [runtime, context])
}
@@ -43,32 +84,32 @@ export class Reffuse<R> {
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<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
const runtime = this.useRuntime()
return React.useMemo(() => runtime.runSync(SubscriptionRef.make(value)), [])
const runSync = this.useRunSync()
return React.useMemo(() => runSync(SubscriptionRef.make(value)), [])
}
useRefFromEffect<A, E>(effect: Effect.Effect<A, E, R>): SubscriptionRef.SubscriptionRef<A> {
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<A>(ref: SubscriptionRef.SubscriptionRef<A>): [A, React.Dispatch<React.SetStateAction<A>>] {
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<R> {
)), [ref])
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
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<R> {
}
export const make = <ROut, E>(layer: Layer.Layer<ROut, E, never>): Reffuse<ROut, E> =>
export const make = <R>(): Reffuse<R> =>
new Reffuse(ManagedRuntime.make(layer))

View File

@@ -0,0 +1,7 @@
import type { Context, Runtime } from "effect"
export interface ReffuseReactContext<R> {
readonly runtime: Runtime.Runtime<never>
readonly context: Context.Context<R>
}