From a95ba9d4b3cc8a13c6cf01e39965b64418dabcab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Fri, 8 Aug 2025 01:00:10 +0200 Subject: [PATCH] Refactoring --- packages/effect-fc/src/Component.ts | 77 ++++++++++++----------------- 1 file changed, 32 insertions(+), 45 deletions(-) diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index 9dd4419..f2773e9 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -1,4 +1,4 @@ -import { Context, Effect, Effectable, ExecutionStrategy, Function, identity, Predicate, Runtime, Scope, String, Tracer, type Types } from "effect" +import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, String, Tracer, type Types } from "effect" import * as React from "react" import { Hooks } from "./hooks/index.js" import * as Memoized from "./Memoized.js" @@ -7,35 +7,35 @@ import * as Memoized from "./Memoized.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Component") export type TypeId = typeof TypeId -export interface Component +export interface Component extends - Effect.Effect>, + Effect.Effect<(props: P) => A, never, Exclude>, Component.Options { new(_: never): {} readonly [TypeId]: TypeId - readonly ["~FunctionComponent"]: F - readonly ["~Props"]: FunctionComponent.Props - readonly ["~Success"]: FunctionComponent.Success + readonly ["~Props"]: P + readonly ["~Success"]: A readonly ["~Error"]: E readonly ["~Context"]: R /** @internal */ - readonly body: (props: FunctionComponent.Props) => Effect.Effect, E, R> + readonly body: (props: P) => Effect.Effect /** @internal */ makeFunctionComponent( runtimeRef: React.Ref>>, scope: Scope.Scope, - ): F + ): (props: P) => A } export namespace Component { - export type FunctionComponent> = T extends Component ? F : never - export type Error> = T extends Component ? E : never - export type Context> = T extends Component ? R : never + export type Props> = T extends Component ? P : never + export type Success> = T extends Component ? A : never + export type Error> = T extends Component ? E : never + export type Context> = T extends Component ? R : never - export type AsComponent> = Component, Error, Context> + export type AsComponent> = Component, Success, Error, Context> export interface Options { readonly displayName?: string @@ -44,26 +44,19 @@ export namespace Component { } } -export type FunctionComponent = (...args: readonly any[]) => React.ReactNode - -export namespace FunctionComponent { - export type Props = T extends (...args: readonly [infer P, ...any[]]) => infer _A ? P : never - export type Success = T extends (...args: infer _Args) => infer A ? A : never -} - const ComponentProto = Object.freeze({ ...Effectable.CommitPrototype, [TypeId]: TypeId, - commit: Effect.fnUntraced(function* ( - this: Component + commit: Effect.fnUntraced(function* ( + this: Component ) { const self = this const runtimeRef = React.useRef>>(null!) runtimeRef.current = yield* Effect.runtime>() - return React.useCallback(function ScopeProvider(props: FunctionComponent.Props) { + return React.useCallback(function ScopeProvider(props: P) { const scope = Runtime.runSync(runtimeRef.current)(Hooks.useScope( Array.from( Context.omit(...nonReactiveTags)(runtimeRef.current.context).unsafeMap.values() @@ -72,23 +65,23 @@ const ComponentProto = Object.freeze({ )) const FC = React.useMemo(() => { - const f: React.FC> = self.makeFunctionComponent(runtimeRef, scope) + const f: React.FC

= self.makeFunctionComponent(runtimeRef, scope) f.displayName = self.displayName ?? "Anonymous" return Memoized.isMemoized(self) ? React.memo(f, self.propsAreEqual) : f }, [scope]) - return React.createElement(FC, props) + return React.createElement(FC as React.FC<{}>, props as {}) }, []) }), - makeFunctionComponent( - this: Component, + makeFunctionComponent( + this: Component, runtimeRef: React.RefObject>>, scope: Scope.Scope, ) { - return (props: FunctionComponent.Props) => Runtime.runSync(runtimeRef.current)( + return (props: P) => Runtime.runSync(runtimeRef.current)( Effect.provideService(this.body(props), Scope.Scope, scope) ) }, @@ -102,7 +95,7 @@ const defaultOptions = { const nonReactiveTags = [Tracer.ParentSpan] as const -export const isComponent = (u: unknown): u is Component => Predicate.hasProperty(u, TypeId) +export const isComponent = (u: unknown): u is Component => Predicate.hasProperty(u, TypeId) export const make = ( body: (...args: Args) => Effect.Effect @@ -114,21 +107,15 @@ export const make = (): >(self: T) => - & Omit> - & Component, Component.Context> -} = () => identity - export const withOptions: { - >( + >( options: Partial ): (self: T) => T - >( + >( self: T, options: Partial, ): T -} = Function.dual(2, >( +} = Function.dual(2, >( self: T, options: Partial, ): T => Object.setPrototypeOf( @@ -137,17 +124,17 @@ export const withOptions: { )) export const withRuntime: { - ( + ( context: React.Context>, - ): (self: Component>) => F - ( - self: Component>, + ): (self: Component>) => (props: P) => A + ( + self: Component>, context: React.Context>, - ): F -} = Function.dual(2, ( - self: Component, + ): (props: P) => A +} = Function.dual(2,

( + self: Component, context: React.Context>, -) => function WithRuntime(props: FunctionComponent.Props) { +) => function WithRuntime(props: P) { return React.createElement( Runtime.runSync(React.useContext(context))(self), props,