diff --git a/packages/effect-fc-next/src/Component.ts b/packages/effect-fc-next/src/Component.ts
index a651525..12002ad 100644
--- a/packages/effect-fc-next/src/Component.ts
+++ b/packages/effect-fc-next/src/Component.ts
@@ -1,6 +1,6 @@
/** biome-ignore-all lint/complexity/noBannedTypes: {} is the default type for React props */
/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
-import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Runtime, Scope, Tracer } from "effect"
+import { Context, type Duration, Effect, Equivalence, ExecutionStrategy, Exit, Fiber, Function, HashMap, identity, Layer, Option, Pipeable, Predicate, Ref, Scope, Tracer } from "effect"
import * as React from "react"
@@ -609,18 +609,18 @@ export const withOptions: {
*/
export const withRuntime: {
(
- context: React.Context>,
+ context: React.Context>,
): (self: Component, F>) => F
(
self: Component
, F>,
- context: React.Context>,
+ context: React.Context>,
): F
} = Function.dual(2, (
self: Component
,
- context: React.Context>,
+ context: React.Context>,
) => function WithRuntime(props: P) {
return React.createElement(
- Runtime.runSync(React.useContext(context))(self.use) as React.FC,
+ Effect.runSyncWith(React.useContext(context))(self.use) as React.FC
,
props,
)
})
diff --git a/packages/effect-fc-next/src/ReactRuntime.ts b/packages/effect-fc-next/src/ReactRuntime.ts
index 92f9108..2c40a78 100644
--- a/packages/effect-fc-next/src/ReactRuntime.ts
+++ b/packages/effect-fc-next/src/ReactRuntime.ts
@@ -1,41 +1,52 @@
-/** biome-ignore-all lint/complexity/useArrowFunction: React component names are intentional */
-import { Context, Layer, ManagedRuntime, Predicate } from "effect"
+/** biome-ignore-all lint/complexity/useArrowFunction: necessary for class prototypes */
+import { type Context, Effect, Layer, ManagedRuntime, Predicate } from "effect"
import * as React from "react"
+import * as Component from "./Component.js"
import * as ErrorObserver from "./ErrorObserver.js"
import * as QueryClient from "./QueryClient.js"
-export const TypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime")
-export type TypeId = typeof TypeId
+export const ReactRuntimeTypeId: unique symbol = Symbol.for("@effect-fc/ReactRuntime/ReactRuntime")
+export type ReactRuntimeTypeId = typeof ReactRuntimeTypeId
export interface ReactRuntime {
new(_: never): Record
- readonly [TypeId]: TypeId
+ readonly [ReactRuntimeTypeId]: ReactRuntimeTypeId
readonly runtime: ManagedRuntime.ManagedRuntime
readonly context: React.Context>
}
-const ReactRuntimeProto = Object.freeze({ [TypeId]: TypeId } as const)
+const ReactRuntimeProto = Object.freeze({ [ReactRuntimeTypeId]: ReactRuntimeTypeId } as const)
-export const Prelude: Layer.Layer = Layer.merge(
+export const preludeLayer: Layer.Layer<
+ | Component.ScopeMap
+ | ErrorObserver.ErrorObserver
+ | QueryClient.QueryClient
+> = Layer.mergeAll(
+ Component.ScopeMap.layer,
ErrorObserver.layer,
QueryClient.QueryClient.Default,
)
+
export const isReactRuntime = (u: unknown): u is ReactRuntime => Predicate.hasProperty(u, TypeId)
export const make = (
layer: Layer.Layer,
memoMap?: Layer.MemoMap,
-): ReactRuntime => Object.setPrototypeOf(
+): ReactRuntime | R, ER> => Object.setPrototypeOf(
Object.assign(function() {}, {
- runtime: ManagedRuntime.make(Layer.merge(layer, Prelude), { memoMap }),
- // biome-ignore lint/style/noNonNullAssertion: initialized by Provider before consumers render
- context: React.createContext>(null!),
+ runtime: ManagedRuntime.make(
+ Layer.merge(layer, preludeLayer),
+ memoMap,
+ ),
+ // biome-ignore lint/style/noNonNullAssertion: context initialization
+ context: React.createContext>(null!),
}),
ReactRuntimeProto,
)
+
export namespace Provider {
export interface Props extends React.SuspenseProps {
readonly runtime: ReactRuntime
@@ -44,8 +55,13 @@ export namespace Provider {
}
export const Provider = (
- { runtime, children, ...suspenseProps }: Provider.Props,
+ { runtime, children, ...suspenseProps }: Provider.Props
): React.ReactNode => {
+ Effect.runSync(Component.useOnChange(
+ () => Effect.addFinalizer(() => runtime.runtime.disposeEffect),
+ [runtime],
+ ))
+
const promise = React.useMemo(() => runtime.runtime.context(), [runtime])
return React.createElement(
@@ -60,12 +76,5 @@ const ProviderInner = (
readonly runtime: ReactRuntime
readonly promise: Promise>
readonly children?: React.ReactNode
- },
-): React.ReactNode => {
- const context = React.use(promise)
- React.useEffect(() => () => {
- void runtime.runtime.dispose()
- }, [runtime])
-
- return React.createElement(runtime.context, { value: context }, children)
-}
+ }
+): React.ReactNode => React.createElement(runtime.context, { value: React.use(promise) }, children)