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 74 additions and 70 deletions
Showing only changes of commit 12849d37da - Show all commits

View File

@@ -1,23 +1,24 @@
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 type * as ReffuseReactContext from "./ReffuseReactContext.js" import * as ReffuseReactContext from "./ReffuseReactContext.js"
import * as ReffuseReactContextProvider from "./ReffuseReactContextProvider.js"
export class Reffuse< export class Reffuse<
R extends RuntimeR | ContextR,
RuntimeR, RuntimeR,
ContextR, ContextR extends ParentContextR | OwnContextR,
Parent extends Reffuse<any, any, any, any>, OwnContextR,
ParentContextR = never,
> { > {
readonly Context = React.createContext<ReffuseReactContext.Value<RuntimeR, ContextR>>(null!) readonly Context = React.createContext<ReffuseReactContext.Value<RuntimeR, ContextR>>(null!)
readonly Provider: ReffuseReactContextProvider.ReffuseReactContextProvider<R> readonly Provider: ReffuseReactContext.Provider<RuntimeR, OwnContextR, ParentContextR>
constructor( constructor(
runtime: Runtime.Runtime<RuntimeR> runtime: Runtime.Runtime<RuntimeR>,
parent?: Reffuse<RuntimeR, ParentContextR, unknown, unknown>,
) { ) {
this.Provider = ReffuseReactContextProvider.make(runtime, this.Context) // TODO: split into makeProvider and makeScopedProvider
this.Provider = ReffuseReactContext.makeProvider(runtime, this.Context, parent)
} }
@@ -33,7 +34,9 @@ export class Reffuse<
useRunSync() { useRunSync() {
const { runtime, context } = React.useContext(this.Context) const { runtime, context } = React.useContext(this.Context)
return React.useCallback(<A, E>(effect: Effect.Effect<A, E, R>): A => effect.pipe( return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, RuntimeR | ContextR>
): A => effect.pipe(
Effect.provide(context), Effect.provide(context),
Runtime.runSync(runtime), Runtime.runSync(runtime),
), [runtime, context]) ), [runtime, context])
@@ -43,7 +46,7 @@ export class Reffuse<
const { runtime, context } = React.useContext(this.Context) const { runtime, context } = React.useContext(this.Context)
return React.useCallback(<A, E>( return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, R>, effect: Effect.Effect<A, E, RuntimeR | ContextR>,
options?: { readonly signal?: AbortSignal }, options?: { readonly signal?: AbortSignal },
): Promise<A> => effect.pipe( ): Promise<A> => effect.pipe(
Effect.provide(context), Effect.provide(context),
@@ -55,7 +58,7 @@ export class Reffuse<
const { runtime, context } = React.useContext(this.Context) const { runtime, context } = React.useContext(this.Context)
return React.useCallback(<A, E>( return React.useCallback(<A, E>(
effect: Effect.Effect<A, E, R>, effect: Effect.Effect<A, E, RuntimeR | ContextR>,
options?: Runtime.RunForkOptions, options?: Runtime.RunForkOptions,
): Fiber.RuntimeFiber<A, E> => effect.pipe( ): Fiber.RuntimeFiber<A, E> => effect.pipe(
Effect.provide(context), Effect.provide(context),
@@ -65,7 +68,7 @@ export class Reffuse<
useFork<A, E>( useFork<A, E>(
self: Effect.Effect<A, E, R | Scope.Scope>, self: Effect.Effect<A, E, RuntimeR | ContextR | Scope.Scope>,
deps?: React.DependencyList, deps?: React.DependencyList,
options?: Runtime.RunForkOptions, options?: Runtime.RunForkOptions,
): void { ): void {
@@ -83,7 +86,7 @@ export class Reffuse<
return React.useMemo(() => runSync(SubscriptionRef.make(value)), []) return React.useMemo(() => runSync(SubscriptionRef.make(value)), [])
} }
useRefFromEffect<A, E>(effect: Effect.Effect<A, E, R>): SubscriptionRef.SubscriptionRef<A> { useRefFromEffect<A, E>(effect: Effect.Effect<A, E, RuntimeR | ContextR>): SubscriptionRef.SubscriptionRef<A> {
const runSync = this.useRunSync() const runSync = this.useRunSync()
return React.useMemo(() => runSync(effect.pipe( return React.useMemo(() => runSync(effect.pipe(

View File

@@ -1,5 +1,6 @@
import { Effect, Runtime, type Context, type Layer } from "effect" import { Context, Effect, Runtime, type Layer } from "effect"
import React from "react" import React from "react"
import type * as Reffuse from "./Reffuse.js"
export interface Value<RuntimeR, ContextR> { export interface Value<RuntimeR, ContextR> {
@@ -8,29 +9,63 @@ export interface Value<RuntimeR, ContextR> {
} }
export interface ProviderProps<R> { export type Provider<
readonly layer: Layer.Layer<R, unknown> 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 readonly children?: React.ReactNode
} }
export type Provider<R> = React.FC<ProviderProps<R>> export function makeProvider<
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 parent
? function ReffuseReactContextProvider(props) {
const parentContext = parent.useContext()
export function makeProvider<R>(
runtime: Runtime.Runtime<never>,
Context: React.Context<Value<R>>,
) {
return function ReffuseReactContextProvider(props: ProviderProps<R>) {
const value = React.useMemo(() => ({ const value = React.useMemo(() => ({
runtime, runtime,
context: Effect.context<R>().pipe( context: Effect.context<ContextR>().pipe(
Effect.provide(props.layer), Effect.provide(props.layer),
Effect.provide(parentContext),
Runtime.runSync(runtime),
),
}), [props.layer, parentContext])
return (
<ReactContext
{...props}
value={value}
/>
)
}
: function ReffuseReactContextProvider(props) {
const value = React.useMemo(() => ({
runtime,
context: Effect.context<ContextR>().pipe(
Effect.provide(props.layer),
Effect.provide(Context.empty() as Context.Context<ParentContextR>), // Required for type safety
Runtime.runSync(runtime), Runtime.runSync(runtime),
), ),
}), [props.layer]) }), [props.layer])
return ( return (
<Context <ReactContext
{...props} {...props}
value={value} value={value}
/> />

View File

@@ -1,34 +0,0 @@
import { Effect, Runtime, type Layer } from "effect"
import React from "react"
import type * as ReffuseReactContext from "./ReffuseReactContext.js"
export interface Props<R> {
readonly layer: Layer.Layer<R, unknown>
readonly children?: React.ReactNode
}
export type ReffuseReactContextProvider<R> = React.FC<Props<R>>
export function make<R>(
runtime: Runtime.Runtime<never>,
Context: React.Context<ReffuseReactContext.ReffuseReactContext<R>>,
) {
return function ReffuseReactContextProvider(props: Props<R>) {
const value = React.useMemo(() => ({
runtime,
context: Effect.context<R>().pipe(
Effect.provide(props.layer),
Runtime.runSync(runtime),
),
}), [props.layer])
return (
<Context
{...props}
value={value}
/>
)
}
}