0.1.0 #1

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

View File

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