From 3a68192a737e1a9d924de1117992b30db1df14d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 12 Jul 2025 01:57:43 +0200 Subject: [PATCH] Component refactoring --- packages/effect-fc/src/Component.ts | 84 +++++++++++++++-------------- packages/example/src/todo/Todo.tsx | 2 + 2 files changed, 47 insertions(+), 39 deletions(-) diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index 1ca3411..80e3997 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -7,17 +7,19 @@ import type { ExcludeKeys } from "./utils.js" export interface Component extends Pipeable.Pipeable { readonly body: (props: P) => Effect.Effect readonly displayName?: string - readonly options: Options + readonly options: Component.Options } -export interface Options { - readonly finalizerExecutionMode: "sync" | "fork" - readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy -} +export namespace Component { + export interface Options { + readonly finalizerExecutionMode: "sync" | "fork" + readonly finalizerExecutionStrategy: ExecutionStrategy.ExecutionStrategy + } -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 Error = T extends Component ? E : never + export type Context = T extends Component ? R : never + export type Props = T extends Component ? P : never +} const ComponentProto = { @@ -27,18 +29,12 @@ const ComponentProto = { const nonReactiveTags = [Tracer.ParentSpan] as const -export interface MakeOptions { - readonly traced?: boolean - readonly finalizerExecutionMode?: "sync" | "fork" - readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy -} - export const make = < Eff extends Utils.YieldWrap>, P extends {} = {}, >( body: (props: P) => Generator, - options?: MakeOptions, + options?: Make.MakeOptions, ): Component< [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? E : never, [Eff] extends [never] ? never : [Eff] extends [Utils.YieldWrap>] ? R : never, @@ -60,13 +56,16 @@ export const make = < }, ComponentProto) } - -export interface Memoized

{ - readonly memo: true - readonly propsAreEqual?: Equivalence.Equivalence

+export namespace Make { + export interface MakeOptions { + readonly traced?: boolean + readonly finalizerExecutionMode?: "sync" | "fork" + readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy + } } -export const memo: { + +export const withRuntime: { ( context: React.Context>, ): (self: Component) => React.FC

@@ -77,11 +76,34 @@ export const memo: { } = Function.dual(2, ( self: Component, context: React.Context>, -): React.FC

=> { - +): React.FC

=> function WithRuntime(props) { + const runtime = React.useContext(context) + return React.createElement(Runtime.runSync(runtime)(useFC(self)), props) }) +export interface Memoized

{ + readonly memo: true + readonly propsAreEqual?: Equivalence.Equivalence

+} + +export const memo: { + >( + propsAreEqual?: Equivalence.Equivalence> + ): (self: T) => T & Memoized> + >( + self: T, + propsAreEqual?: Equivalence.Equivalence>, + ): T & Memoized> +} = Function.dual(2, >( + self: T, + propsAreEqual?: Equivalence.Equivalence>, +): T & Memoized> => Object.assign( + Object.create(Object.getPrototypeOf(self)), + { ...self, memo: true, propsAreEqual }, +)) + + export const useFC: { ( self: Component @@ -139,7 +161,7 @@ export const useSuspenseFC: { const f = ({ suspenseProps, ...props }: P & { readonly suspenseProps: React.SuspenseProps }) => { const promise = Runtime.runPromise(runtimeRef.current)( - Effect.provideService(self.body(props as P), Scope.Scope, scope) + Effect.provideService(self.body(props as any as P), Scope.Scope, scope) ) return React.createElement( @@ -164,19 +186,3 @@ export const use: { } = Effect.fn("use")(function*(self, fn) { return fn(yield* useFC(self)) }) - -export const withRuntime: { - ( - context: React.Context>, - ): (self: Component) => React.FC

- ( - self: Component, - context: React.Context>, - ): React.FC

-} = Function.dual(2, ( - self: Component, - context: React.Context>, -): React.FC

=> function WithRuntime(props) { - const runtime = React.useContext(context) - return React.createElement(Runtime.runSync(runtime)(useFC(self)), props) -}) diff --git a/packages/example/src/todo/Todo.tsx b/packages/example/src/todo/Todo.tsx index 7767e32..b7d1746 100644 --- a/packages/example/src/todo/Todo.tsx +++ b/packages/example/src/todo/Todo.tsx @@ -111,3 +111,5 @@ export const Todo = Component.make(function* Todo(props: TodoProps) { ) }) + +const MemoizedTodo = Component.memo(Todo)