Working Reffuse
Some checks failed
Lint / lint (push) Failing after 10s

This commit is contained in:
Julien Valverdé
2025-01-15 16:41:23 +01:00
parent 15c1fdd54c
commit 3cc34a2ed1
2 changed files with 26 additions and 132 deletions

View File

@@ -1,51 +1,27 @@
import { Context, Effect, Fiber, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" import { Context, Effect, Fiber, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
import React from "react" import React from "react"
import * as ReffuseReactContext from "./ReffuseReactContext.js" import * as ReffuseContext from "./ReffuseContext.js"
import * as ReffuseRuntime from "./ReffuseRuntime.js"
export class Reffuse< export class Reffuse<R> {
RuntimeR,
ContextR extends ParentContextR | OwnContextR,
OwnContextR,
ParentContextR = never,
> {
readonly Context = React.createContext<ReffuseReactContext.Value<RuntimeR, ContextR>>(null!)
readonly Provider: ReffuseReactContext.Provider<RuntimeR, OwnContextR, ParentContextR>
constructor( constructor(
private readonly runtime: Runtime.Runtime<RuntimeR>, readonly contexts: readonly ReffuseContext.ReffuseContext<R>[]
parent?: Reffuse<RuntimeR, ParentContextR, unknown, unknown>, ) {}
) {
this.Provider = parent
? ReffuseReactContext.makeNestedProvider(runtime, this.Context, parent)
: ReffuseReactContext.makeRootProvider(runtime, this.Context)
}
extend<OwnContextR = never>() {
return new Reffuse<
RuntimeR,
ContextR | OwnContextR,
OwnContextR,
ContextR
>(this.runtime, this)
}
useRuntime(): Runtime.Runtime<RuntimeR> { useContext(): Context.Context<R> {
return React.useContext(this.Context).runtime return ReffuseContext.useMergeAll(...this.contexts)
}
useContext(): Context.Context<ContextR> {
return React.useContext(this.Context).context
} }
useRunSync() { useRunSync() {
const { runtime, context } = React.useContext(this.Context) const runtime = ReffuseRuntime.useRuntime()
const context = this.useContext()
return React.useCallback(<A, E>( return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR> effect: Effect.Effect<A, E, R>
): A => effect.pipe( ): A => effect.pipe(
Effect.provide(context), Effect.provide(context),
Runtime.runSync(runtime), Runtime.runSync(runtime),
@@ -53,10 +29,11 @@ export class Reffuse<
} }
useRunPromise() { useRunPromise() {
const { runtime, context } = React.useContext(this.Context) const runtime = ReffuseRuntime.useRuntime()
const context = this.useContext()
return React.useCallback(<A, E>( return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR>, effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal }, options?: { readonly signal?: AbortSignal },
): Promise<A> => effect.pipe( ): Promise<A> => effect.pipe(
Effect.provide(context), Effect.provide(context),
@@ -65,10 +42,11 @@ export class Reffuse<
} }
useRunFork() { useRunFork() {
const { runtime, context } = React.useContext(this.Context) const runtime = ReffuseRuntime.useRuntime()
const context = this.useContext()
return React.useCallback(<A, E>( return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR>, effect: Effect.Effect<A, E, R>,
options?: Runtime.RunForkOptions, options?: Runtime.RunForkOptions,
): Fiber.RuntimeFiber<A, E> => effect.pipe( ): Fiber.RuntimeFiber<A, E> => effect.pipe(
Effect.provide(context), Effect.provide(context),
@@ -78,7 +56,7 @@ export class Reffuse<
useMemo<A, E>( useMemo<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR>, effect: Effect.Effect<A, E, R>,
deps?: React.DependencyList, deps?: React.DependencyList,
options?: RenderOptions, options?: RenderOptions,
): A { ): A {
@@ -104,7 +82,7 @@ export class Reffuse<
// } // }
useSuspense<A, E>( useSuspense<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR>, effect: Effect.Effect<A, E, R>,
options?: { readonly signal?: AbortSignal }, options?: { readonly signal?: AbortSignal },
): A { ): A {
const runPromise = this.useRunPromise() const runPromise = this.useRunPromise()
@@ -112,7 +90,7 @@ export class Reffuse<
} }
useFork<A, E>( useFork<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR | Scope.Scope>, effect: Effect.Effect<A, E, R | Scope.Scope>,
deps?: React.DependencyList, deps?: React.DependencyList,
options?: Runtime.RunForkOptions & RenderOptions, options?: Runtime.RunForkOptions & RenderOptions,
): void { ): void {
@@ -136,7 +114,7 @@ export class Reffuse<
) )
} }
useRefFromEffect<A, E>(effect: Effect.Effect<A, E, RuntimeR | ContextR>): SubscriptionRef.SubscriptionRef<A> { useRefFromEffect<A, E>(effect: Effect.Effect<A, E, R>): SubscriptionRef.SubscriptionRef<A> {
return this.useMemo( return this.useMemo(
effect.pipe(Effect.flatMap(SubscriptionRef.make)), effect.pipe(Effect.flatMap(SubscriptionRef.make)),
[], [],
@@ -174,9 +152,9 @@ export interface RenderOptions {
} }
export const make = <R = never>(): Reffuse<never, R, R> => export const make = <
new Reffuse(Runtime.defaultRuntime) const Contexts extends readonly ReffuseContext.ReffuseContext<any>[]
>(
export const makeWithRuntime = <R = never>() => contexts: Contexts
<RuntimeR>(runtime: Runtime.Runtime<RuntimeR>): Reffuse<RuntimeR, R, R> => ): Reffuse<{ [K in keyof Contexts]: ReffuseContext.R<Contexts[K]> }[number]> =>
new Reffuse(runtime) new Reffuse(contexts)

View File

@@ -1,84 +0,0 @@
import { Context, Effect, Runtime, type Layer } from "effect"
import React from "react"
import type * as Reffuse from "./Reffuse.js"
export interface Value<RuntimeR, ContextR> {
readonly runtime: Runtime.Runtime<RuntimeR>
readonly context: Context.Context<ContextR>
}
export type Provider<
RuntimeR,
OwnContextR,
ParentContextR,
> = React.FC<ProviderProps<RuntimeR, OwnContextR, ParentContextR>>
export interface ProviderProps<
RuntimeR,
OwnContextR,
ParentContextR,
> {
readonly layer: Layer.Layer<OwnContextR, unknown, RuntimeR | ParentContextR>
readonly children?: React.ReactNode
}
export function makeRootProvider<
RuntimeR,
ContextR extends ParentContextR | OwnContextR,
OwnContextR,
ParentContextR,
>(
runtime: Runtime.Runtime<RuntimeR>,
ReactContext: React.Context<Value<RuntimeR, ContextR>>,
): Provider<RuntimeR, OwnContextR, ParentContextR> {
return function ReffuseRootReactContextProvider(props) {
const value = React.useMemo(() => ({
runtime,
context: Effect.context<ContextR>().pipe(
Effect.provide(props.layer),
Effect.provide(Context.empty() as Context.Context<ParentContextR>), // Just there for type safety. ParentContextR is always never here anyway
Runtime.runSync(runtime),
),
}), [props.layer])
return (
<ReactContext
{...props}
value={value}
/>
)
}
}
export function makeNestedProvider<
RuntimeR,
ContextR extends ParentContextR | OwnContextR,
OwnContextR,
ParentContextR,
>(
runtime: Runtime.Runtime<RuntimeR>,
ReactContext: React.Context<Value<RuntimeR, ContextR>>,
parent: Reffuse.Reffuse<RuntimeR, ParentContextR, unknown, unknown>,
): Provider<RuntimeR, OwnContextR, ParentContextR> {
return function ReffuseNestedReactContextProvider(props) {
const parentContext = parent.useContext()
const value = React.useMemo(() => ({
runtime,
context: Effect.context<ContextR>().pipe(
Effect.provide(props.layer),
Effect.provide(parentContext),
Runtime.runSync(runtime),
),
}), [props.layer, parentContext])
return (
<ReactContext
{...props}
value={value}
/>
)
}
}