diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts index bc637f4..d120975 100644 --- a/packages/effect-fc/src/Component.ts +++ b/packages/effect-fc/src/Component.ts @@ -1,7 +1,7 @@ import { Context, Effect, Effectable, ExecutionStrategy, Function, Predicate, Runtime, Scope, String, Tracer, type Types, type Utils } from "effect" import * as React from "react" import { Hooks } from "./hooks/index.js" -import * as Memoized from "./Memoized.js" +import * as Memo from "./Memo.js" export const TypeId: unique symbol = Symbol.for("effect-fc/Component") @@ -67,7 +67,7 @@ const ComponentProto = Object.freeze({ const FC = React.useMemo(() => { const f: React.FC

= self.makeFunctionComponent(runtimeRef, scope) f.displayName = self.displayName ?? "Anonymous" - return Memoized.isMemoized(self) + return Memo.isMemo(self) ? React.memo(f, self.propsAreEqual) : f }, [scope]) diff --git a/packages/effect-fc/src/Memo.ts b/packages/effect-fc/src/Memo.ts new file mode 100644 index 0000000..4b25df7 --- /dev/null +++ b/packages/effect-fc/src/Memo.ts @@ -0,0 +1,47 @@ +import { type Equivalence, Function, Predicate } from "effect" +import type * as Component from "./Component.js" + + +export const TypeId: unique symbol = Symbol.for("effect-fc/Memo") +export type TypeId = typeof TypeId + +export interface Memo

extends Memo.Options

{ + readonly [TypeId]: TypeId +} + +export namespace Memo { + export interface Options

{ + readonly propsAreEqual?: Equivalence.Equivalence

+ } +} + + +const MemoProto = Object.freeze({ + [TypeId]: TypeId +} as const) + + +export const isMemo = (u: unknown): u is Memo => Predicate.hasProperty(u, TypeId) + +export const memo = >( + self: T +): T & Memo> => Object.setPrototypeOf( + Object.assign(function() {}, self), + Object.freeze({ ...Object.getPrototypeOf(self), ...MemoProto }), +) + +export const withOptions: { + & Memo>( + options: Partial>> + ): (self: T) => T + & Memo>( + self: T, + options: Partial>>, + ): T +} = Function.dual(2, & Memo>( + self: T, + options: Partial>>, +): T => Object.setPrototypeOf( + Object.assign(function() {}, self, options), + Object.getPrototypeOf(self), +)) diff --git a/packages/effect-fc/src/Memoized.ts b/packages/effect-fc/src/Memoized.ts deleted file mode 100644 index 86cab06..0000000 --- a/packages/effect-fc/src/Memoized.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { type Equivalence, Function, Predicate } from "effect" -import type * as Component from "./Component.js" - - -export const TypeId: unique symbol = Symbol.for("effect-fc/Memoized") -export type TypeId = typeof TypeId - -export interface Memoized

extends Memoized.Options

{ - readonly [TypeId]: TypeId -} - -export namespace Memoized { - export interface Options

{ - readonly propsAreEqual?: Equivalence.Equivalence

- } -} - - -const MemoizedProto = Object.freeze({ - [TypeId]: TypeId -} as const) - - -export const isMemoized = (u: unknown): u is Memoized => Predicate.hasProperty(u, TypeId) - -export const memo = >( - self: T -): T & Memoized> => Object.setPrototypeOf( - Object.assign(function() {}, self), - Object.freeze({ ...Object.getPrototypeOf(self), ...MemoizedProto }), -) - -export const withOptions: { - & Memoized>( - options: Partial>> - ): (self: T) => T - & Memoized>( - self: T, - options: Partial>>, - ): T -} = Function.dual(2, & Memoized>( - self: T, - options: Partial>>, -): T => Object.setPrototypeOf( - Object.assign(function() {}, self, options), - Object.getPrototypeOf(self), -)) diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts index 9676bf1..a9be6ce 100644 --- a/packages/effect-fc/src/index.ts +++ b/packages/effect-fc/src/index.ts @@ -1,4 +1,4 @@ export * as Component from "./Component.js" -export * as Memoized from "./Memoized.js" +export * as Memo from "./Memo.js" export * as ReactManagedRuntime from "./ReactManagedRuntime.js" export * as Suspense from "./Suspense.js" diff --git a/packages/example/src/routes/dev/async-rendering.tsx b/packages/example/src/routes/dev/async-rendering.tsx index 64ad8ea..3728739 100644 --- a/packages/example/src/routes/dev/async-rendering.tsx +++ b/packages/example/src/routes/dev/async-rendering.tsx @@ -3,7 +3,7 @@ import { Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Effect } from "effect" -import { Component, Memoized, Suspense } from "effect-fc" +import { Component, Memo, Suspense } from "effect-fc" import { Hooks } from "effect-fc/hooks" import * as React from "react" @@ -67,7 +67,7 @@ class AsyncComponent extends Component.makeUntraced(function* AsyncComponent() { Suspense.suspense, Suspense.withOptions({ defaultFallback:

Loading...

}), ) {} -class MemoizedAsyncComponent extends Memoized.memo(AsyncComponent) {} +class MemoizedAsyncComponent extends Memo.memo(AsyncComponent) {} class SubComponent extends Component.makeUntraced(function* SubComponent() { const [state] = React.useState(yield* Hooks.useOnce(() => Effect.provide(makeUuid4, GetRandomValues.CryptoRandom))) diff --git a/packages/example/src/routes/dev/memo.tsx b/packages/example/src/routes/dev/memo.tsx index b5a8d50..4413107 100644 --- a/packages/example/src/routes/dev/memo.tsx +++ b/packages/example/src/routes/dev/memo.tsx @@ -3,7 +3,7 @@ import { Flex, Text, TextField } from "@radix-ui/themes" import { createFileRoute } from "@tanstack/react-router" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Effect } from "effect" -import { Component, Memoized } from "effect-fc" +import { Component, Memo } from "effect-fc" import * as React from "react" @@ -30,7 +30,7 @@ class SubComponent extends Component.makeUntraced(function* SubComponent() { return {id} }) {} -class MemoizedSubComponent extends Memoized.memo(SubComponent) {} +class MemoizedSubComponent extends Memo.memo(SubComponent) {} export const Route = createFileRoute("/dev/memo")({ component: RouteComponent, diff --git a/packages/example/src/todo/Todo.tsx b/packages/example/src/todo/Todo.tsx index 3eba9c8..89ad896 100644 --- a/packages/example/src/todo/Todo.tsx +++ b/packages/example/src/todo/Todo.tsx @@ -2,7 +2,7 @@ import * as Domain from "@/domain" import { Box, Button, Callout, Flex, IconButton, Text, TextArea } from "@radix-ui/themes" import { GetRandomValues, makeUuid4 } from "@typed/id" import { Chunk, Effect, Match, Option, ParseResult, Ref, Runtime, Schema, SubscriptionRef } from "effect" -import { Component, Memoized } from "effect-fc" +import { Component, Memo } from "effect-fc" import { Hooks } from "effect-fc/hooks" import { SubscriptionSubRef } from "effect-fc/types" import { FaArrowDown, FaArrowUp } from "react-icons/fa" @@ -123,5 +123,5 @@ export class Todo extends Component.makeUntraced(function* Todo(props: TodoProps ) }).pipe( - Memoized.memo + Memo.memo ) {}