diff --git a/packages/effect-fc/src/Component.ts b/packages/effect-fc/src/Component.ts
index 548182f..1dbd9b7 100644
--- a/packages/effect-fc/src/Component.ts
+++ b/packages/effect-fc/src/Component.ts
@@ -1,7 +1,8 @@
-import { Context, Effect, type Equivalence, ExecutionStrategy, Function, pipe, Pipeable, Predicate, Runtime, Scope, String, Tracer, type Utils } from "effect"
+import { Context, Effect, ExecutionStrategy, Function, pipe, Pipeable, Predicate, Runtime, Scope, String, Tracer, type Utils } from "effect"
import * as React from "react"
import * as Hook from "./Hook.js"
-import type { ExcludeKeys } from "./utils.js"
+import * as Memoized from "./Memoized.js"
+import * as Suspense from "./Suspense.js"
export const TypeId: unique symbol = Symbol.for("effect-fc/Component")
@@ -339,93 +340,15 @@ export const withOptions: {
))
-export interface Memoized
{
- readonly memo: true
- readonly memoOptions: Memoized.Options
-}
-
-export namespace Memoized {
- export interface Options
{
- readonly propsAreEqual?: Equivalence.Equivalence
- }
-}
-
-export const memo = | Component & Memoized>(
- self: T
-): T & Memoized> => Object.setPrototypeOf({
- ...self,
- memo: true,
- memoOptions: Predicate.hasProperty(self, "memo") ? { ...self.memoOptions } : {},
-}, Object.getPrototypeOf(self))
-
-export const withMemoOptions: {
- & Memoized>(
- memoOptions: Partial>>
- ): (self: T) => T
- & Memoized>(
- self: T,
- memoOptions: Partial>>,
- ): T
-} = Function.dual(2, & Memoized>(
- self: T,
- memoOptions: Partial>>,
-): T => Object.setPrototypeOf(
- { ...self, memoOptions: { ...self.memoOptions, ...memoOptions } },
- Object.getPrototypeOf(self),
-))
-
-
-export interface Suspense {
- readonly suspense: true
- readonly suspenseOptions: Suspense.Options
-}
-
-export namespace Suspense {
- export interface Options {
- readonly defaultFallback?: React.ReactNode
- }
-
- export type Props = Omit
-}
-
-export const suspense = | Component & Suspense, P extends {}>(
- self: T & Component>
-): (
- & T
- & Component, Component.Context, P & Suspense.Props>
- & Suspense
-) => Object.setPrototypeOf({
- ...self,
- suspense: true,
- suspenseOptions: Predicate.hasProperty(self, "suspense") ? { ...self.suspenseOptions } : {},
-}, Object.getPrototypeOf(self))
-
-export const withSuspenseOptions: {
- & Suspense>(
- suspenseOptions: Partial
- ): (self: T) => T
- & Suspense>(
- self: T,
- suspenseOptions: Partial,
- ): T
-} = Function.dual(2, & Suspense>(
- self: T,
- suspenseOptions: Partial,
-): T => Object.setPrototypeOf(
- { ...self, suspense: true, suspenseOptions: { ...self.suspenseOptions, ...suspenseOptions } },
- Object.getPrototypeOf(self),
-))
-
-
export const useFC: {
(
- self: Component & Suspense
- ): Effect.Effect, never, Exclude>
+ self: Component & Suspense.Suspense
+ ): Effect.Effect, never, Exclude>
(
self: Component
): Effect.Effect, never, Exclude>
} = Effect.fn("useFC")(function* (
- self: Component & (Memoized | Suspense | {})
+ self: Component & (Memoized.Memoized | Suspense.Suspense | {})
) {
const runtimeRef = React.useRef>>(null!)
runtimeRef.current = yield* Effect.runtime>()
@@ -439,20 +362,20 @@ export const useFC: {
))
const FC = React.useMemo(() => {
- const f: React.FC = Predicate.hasProperty(self, "suspense")
+ const f: React.FC
= Suspense.isSuspense(self)
? pipe(
function SuspenseInner(props: { readonly promise: Promise }) {
return React.use(props.promise)
},
- SuspenseInner => ({ fallback, name, ...props }: P & Suspense.Props) => {
+ SuspenseInner => ({ fallback, name, ...props }: P & Suspense.Suspense.Props) => {
const promise = Runtime.runPromise(runtimeRef.current)(
Effect.provideService(self.body(props as P), Scope.Scope, scope)
)
return React.createElement(
React.Suspense,
- { fallback: fallback ?? self.suspenseOptions.defaultFallback, name },
+ { fallback: fallback ?? self.defaultFallback, name },
React.createElement(SuspenseInner, { promise }),
)
},
@@ -462,8 +385,8 @@ export const useFC: {
)
f.displayName = self.displayName ?? "Anonymous"
- return Predicate.hasProperty(self, "memo")
- ? React.memo(f, self.memoOptions.propsAreEqual)
+ return Memoized.isMemoized(self)
+ ? React.memo(f, self.propsAreEqual)
: f
}, [scope])
@@ -473,8 +396,8 @@ export const useFC: {
export const use: {
(
- self: Component & Suspense,
- fn: (Component: React.FC) => React.ReactNode,
+ self: Component & Suspense.Suspense,
+ fn: (Component: React.FC) => React.ReactNode,
): Effect.Effect>
(
self: Component,
@@ -487,14 +410,14 @@ export const use: {
export const withRuntime: {
, R>(
context: React.Context>,
- ): (self: T) => React.FC & Suspense.Props
+ ): (self: T) => React.FC & Suspense.Suspense.Props
: Component.Props
>
(
- self: Component & Suspense,
+ self: Component & Suspense.Suspense,
context: React.Context>,
- ): React.FC
+ ): React.FC
(
self: Component,
context: React.Context>,
diff --git a/packages/effect-fc/src/Memoized.ts b/packages/effect-fc/src/Memoized.ts
new file mode 100644
index 0000000..4400c34
--- /dev/null
+++ b/packages/effect-fc/src/Memoized.ts
@@ -0,0 +1,42 @@
+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]: true
+}
+
+export namespace Memoized {
+ export interface Options
{
+ readonly propsAreEqual?: Equivalence.Equivalence
+ }
+}
+
+
+export const isMemoized = (u: unknown): u is Memoized => Predicate.hasProperty(u, TypeId)
+
+export const memo = >(
+ self: T
+): T & Memoized> => Object.setPrototypeOf(
+ { ...self, [TypeId]: true },
+ Object.getPrototypeOf(self),
+)
+
+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(
+ { ...self, ...options },
+ Object.getPrototypeOf(self),
+))
diff --git a/packages/effect-fc/src/Suspense.ts b/packages/effect-fc/src/Suspense.ts
new file mode 100644
index 0000000..b6aeba8
--- /dev/null
+++ b/packages/effect-fc/src/Suspense.ts
@@ -0,0 +1,49 @@
+import { Function, Predicate } from "effect"
+import type * as Component from "./Component.js"
+import type { ExcludeKeys } from "./utils.js"
+
+
+export const TypeId: unique symbol = Symbol.for("effect-fc/Suspense")
+export type TypeId = typeof TypeId
+
+export interface Suspense extends Suspense.Options {
+ readonly [TypeId]: true
+}
+
+export namespace Suspense {
+ export interface Options {
+ readonly defaultFallback?: React.ReactNode
+ }
+
+ export type Props = Omit
+}
+
+
+export const isSuspense = (u: unknown): u is Suspense => Predicate.hasProperty(u, TypeId)
+
+export const suspense = , P extends {}>(
+ self: T & Component.Component>
+): (
+ & T
+ & Component.Component, Component.Component.Context, P & Suspense.Props>
+ & Suspense
+) => Object.setPrototypeOf(
+ { ...self, [TypeId]: true },
+ Object.getPrototypeOf(self),
+)
+
+export const withOptions: {
+ & Suspense>(
+ options: Partial
+ ): (self: T) => T
+ & Suspense>(
+ self: T,
+ options: Partial,
+ ): T
+} = Function.dual(2, & Suspense>(
+ self: T,
+ options: Partial,
+): T => Object.setPrototypeOf(
+ { ...self, ...options },
+ Object.getPrototypeOf(self),
+))
diff --git a/packages/effect-fc/src/index.ts b/packages/effect-fc/src/index.ts
index 4318eff..fd1ade9 100644
--- a/packages/effect-fc/src/index.ts
+++ b/packages/effect-fc/src/index.ts
@@ -1,3 +1,5 @@
export * as Component from "./Component.js"
export * as Hook from "./Hook.js"
+export * as Memoized from "./Memoized.js"
export * as ReactManagedRuntime from "./ReactManagedRuntime.js"
+export * as Suspense from "./Suspense.js"