From 12849d37da22bd4b763158e59d5a62eac1fbae18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Tue, 14 Jan 2025 00:54:39 +0100 Subject: [PATCH] Work --- packages/reffuse/src/Reffuse.ts | 29 ++++--- packages/reffuse/src/ReffuseReactContext.tsx | 81 +++++++++++++------ .../src/ReffuseReactContextProvider.tsx | 34 -------- 3 files changed, 74 insertions(+), 70 deletions(-) delete mode 100644 packages/reffuse/src/ReffuseReactContextProvider.tsx diff --git a/packages/reffuse/src/Reffuse.ts b/packages/reffuse/src/Reffuse.ts index 5b5a4e9..19a4748 100644 --- a/packages/reffuse/src/Reffuse.ts +++ b/packages/reffuse/src/Reffuse.ts @@ -1,23 +1,24 @@ import { Context, Effect, Fiber, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect" import React from "react" -import type * as ReffuseReactContext from "./ReffuseReactContext.js" -import * as ReffuseReactContextProvider from "./ReffuseReactContextProvider.js" +import * as ReffuseReactContext from "./ReffuseReactContext.js" export class Reffuse< - R extends RuntimeR | ContextR, RuntimeR, - ContextR, - Parent extends Reffuse, + ContextR extends ParentContextR | OwnContextR, + OwnContextR, + ParentContextR = never, > { readonly Context = React.createContext>(null!) - readonly Provider: ReffuseReactContextProvider.ReffuseReactContextProvider + readonly Provider: ReffuseReactContext.Provider constructor( - runtime: Runtime.Runtime + runtime: Runtime.Runtime, + parent?: Reffuse, ) { - 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() { const { runtime, context } = React.useContext(this.Context) - return React.useCallback((effect: Effect.Effect): A => effect.pipe( + return React.useCallback(( + effect: Effect.Effect + ): A => effect.pipe( Effect.provide(context), Runtime.runSync(runtime), ), [runtime, context]) @@ -43,7 +46,7 @@ export class Reffuse< const { runtime, context } = React.useContext(this.Context) return React.useCallback(( - effect: Effect.Effect, + effect: Effect.Effect, options?: { readonly signal?: AbortSignal }, ): Promise => effect.pipe( Effect.provide(context), @@ -55,7 +58,7 @@ export class Reffuse< const { runtime, context } = React.useContext(this.Context) return React.useCallback(( - effect: Effect.Effect, + effect: Effect.Effect, options?: Runtime.RunForkOptions, ): Fiber.RuntimeFiber => effect.pipe( Effect.provide(context), @@ -65,7 +68,7 @@ export class Reffuse< useFork( - self: Effect.Effect, + self: Effect.Effect, deps?: React.DependencyList, options?: Runtime.RunForkOptions, ): void { @@ -83,7 +86,7 @@ export class Reffuse< return React.useMemo(() => runSync(SubscriptionRef.make(value)), []) } - useRefFromEffect(effect: Effect.Effect): SubscriptionRef.SubscriptionRef { + useRefFromEffect(effect: Effect.Effect): SubscriptionRef.SubscriptionRef { const runSync = this.useRunSync() return React.useMemo(() => runSync(effect.pipe( diff --git a/packages/reffuse/src/ReffuseReactContext.tsx b/packages/reffuse/src/ReffuseReactContext.tsx index 9d6ea48..dee25a0 100644 --- a/packages/reffuse/src/ReffuseReactContext.tsx +++ b/packages/reffuse/src/ReffuseReactContext.tsx @@ -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 type * as Reffuse from "./Reffuse.js" export interface Value { @@ -8,32 +9,66 @@ export interface Value { } -export interface ProviderProps { - readonly layer: Layer.Layer +export type Provider< + RuntimeR, + OwnContextR, + ParentContextR, +> = React.FC> + +export interface ProviderProps< + RuntimeR, + OwnContextR, + ParentContextR, +> { + readonly layer: Layer.Layer readonly children?: React.ReactNode } -export type Provider = React.FC> +export function makeProvider< + RuntimeR, + ContextR extends ParentContextR | OwnContextR, + OwnContextR, + ParentContextR, +>( + runtime: Runtime.Runtime, + ReactContext: React.Context>, + parent?: Reffuse.Reffuse, +): Provider { + return parent + ? function ReffuseReactContextProvider(props) { + const parentContext = parent.useContext() + const value = React.useMemo(() => ({ + runtime, + context: Effect.context().pipe( + Effect.provide(props.layer), + Effect.provide(parentContext), + Runtime.runSync(runtime), + ), + }), [props.layer, parentContext]) -export function makeProvider( - runtime: Runtime.Runtime, - Context: React.Context>, -) { - return function ReffuseReactContextProvider(props: ProviderProps) { - const value = React.useMemo(() => ({ - runtime, - context: Effect.context().pipe( - Effect.provide(props.layer), - Runtime.runSync(runtime), - ), - }), [props.layer]) + return ( + + ) + } + : function ReffuseReactContextProvider(props) { + const value = React.useMemo(() => ({ + runtime, + context: Effect.context().pipe( + Effect.provide(props.layer), + Effect.provide(Context.empty() as Context.Context), // Required for type safety + Runtime.runSync(runtime), + ), + }), [props.layer]) - return ( - - ) - } + return ( + + ) + } } diff --git a/packages/reffuse/src/ReffuseReactContextProvider.tsx b/packages/reffuse/src/ReffuseReactContextProvider.tsx deleted file mode 100644 index f0d0ad7..0000000 --- a/packages/reffuse/src/ReffuseReactContextProvider.tsx +++ /dev/null @@ -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 { - readonly layer: Layer.Layer - readonly children?: React.ReactNode -} - -export type ReffuseReactContextProvider = React.FC> - - -export function make( - runtime: Runtime.Runtime, - Context: React.Context>, -) { - return function ReffuseReactContextProvider(props: Props) { - const value = React.useMemo(() => ({ - runtime, - context: Effect.context().pipe( - Effect.provide(props.layer), - Runtime.runSync(runtime), - ), - }), [props.layer]) - - return ( - - ) - } -}