This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { Context, Effect, ExecutionStrategy, Exit, Function, Pipeable, Ref, Runtime, Scope, String, Tracer, type Types, type Utils } from "effect"
|
import { Context, Effect, ExecutionStrategy, Function, Pipeable, Runtime, Scope, String, Tracer, type Types, type Utils } from "effect"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
import * as Hook from "./Hook.js"
|
||||||
|
|
||||||
|
|
||||||
export interface Component<E, R, P extends {}> extends Pipeable.Pipeable {
|
export interface Component<E, R, P extends {}> extends Pipeable.Pipeable {
|
||||||
@@ -68,7 +69,7 @@ export const useFC: {
|
|||||||
runtimeRef.current = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
runtimeRef.current = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
||||||
|
|
||||||
return React.useMemo(() => function ScopeProvider(props: P) {
|
return React.useMemo(() => function ScopeProvider(props: P) {
|
||||||
const scope = useScope(runtimeRef.current, self.options)
|
const scope = Runtime.runSync(runtimeRef.current)(Hook.useScope([], self.options))
|
||||||
|
|
||||||
const FC = React.useMemo(() => {
|
const FC = React.useMemo(() => {
|
||||||
const f = (props: P) => Runtime.runSync(runtimeRef.current)(
|
const f = (props: P) => Runtime.runSync(runtimeRef.current)(
|
||||||
@@ -84,48 +85,6 @@ export const useFC: {
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
const useScope = (
|
|
||||||
runtime: Runtime.Runtime<never>,
|
|
||||||
options: Options,
|
|
||||||
) => {
|
|
||||||
const [isInitialRun, initialScope] = React.useMemo(() => Runtime.runSync(runtime)(
|
|
||||||
Effect.all([Ref.make(true), Scope.make(options.finalizerExecutionStrategy)])
|
|
||||||
), [])
|
|
||||||
const [scope, setScope] = React.useState(initialScope)
|
|
||||||
|
|
||||||
React.useEffect(() => Runtime.runSync(runtime)(
|
|
||||||
Effect.if(isInitialRun, {
|
|
||||||
onTrue: () => Effect.as(
|
|
||||||
Ref.set(isInitialRun, false),
|
|
||||||
() => closeScope(scope, runtime, options),
|
|
||||||
),
|
|
||||||
|
|
||||||
onFalse: () => Scope.make(options.finalizerExecutionStrategy).pipe(
|
|
||||||
Effect.tap(scope => Effect.sync(() => setScope(scope))),
|
|
||||||
Effect.map(scope => () => closeScope(scope, runtime, options)),
|
|
||||||
),
|
|
||||||
})
|
|
||||||
), [])
|
|
||||||
|
|
||||||
return scope
|
|
||||||
}
|
|
||||||
|
|
||||||
const closeScope = (
|
|
||||||
scope: Scope.CloseableScope,
|
|
||||||
runtime: Runtime.Runtime<never>,
|
|
||||||
options: Options,
|
|
||||||
) => {
|
|
||||||
switch (options.finalizerExecutionMode) {
|
|
||||||
case "sync":
|
|
||||||
Runtime.runSync(runtime)(Scope.close(scope, Exit.void))
|
|
||||||
break
|
|
||||||
case "fork":
|
|
||||||
Runtime.runFork(runtime)(Scope.close(scope, Exit.void))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const use: {
|
export const use: {
|
||||||
<E, R, P extends {}>(
|
<E, R, P extends {}>(
|
||||||
self: Component<E, R, P>,
|
self: Component<E, R, P>,
|
||||||
|
|||||||
@@ -9,6 +9,53 @@ export interface ScopeOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const useScope: {
|
||||||
|
(
|
||||||
|
deps: React.DependencyList,
|
||||||
|
options?: ScopeOptions,
|
||||||
|
): Effect.Effect<Scope.Scope>
|
||||||
|
} = Effect.fn("useScope")(function*(deps, options) {
|
||||||
|
const runtime = yield* Effect.runtime()
|
||||||
|
|
||||||
|
const [isInitialRun, initialScope] = React.useMemo(() => Runtime.runSync(runtime)(Effect.all([
|
||||||
|
Ref.make(true),
|
||||||
|
Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.parallel),
|
||||||
|
])), [])
|
||||||
|
const [scope, setScope] = React.useState(initialScope)
|
||||||
|
|
||||||
|
React.useEffect(() => Runtime.runSync(runtime)(
|
||||||
|
Effect.if(isInitialRun, {
|
||||||
|
onTrue: () => Effect.as(
|
||||||
|
Ref.set(isInitialRun, false),
|
||||||
|
() => closeScope(scope, runtime, options),
|
||||||
|
),
|
||||||
|
|
||||||
|
onFalse: () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.parallel).pipe(
|
||||||
|
Effect.tap(scope => Effect.sync(() => setScope(scope))),
|
||||||
|
Effect.map(scope => () => closeScope(scope, runtime, options)),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
), deps)
|
||||||
|
|
||||||
|
return scope
|
||||||
|
})
|
||||||
|
|
||||||
|
const closeScope = (
|
||||||
|
scope: Scope.CloseableScope,
|
||||||
|
runtime: Runtime.Runtime<never>,
|
||||||
|
options?: ScopeOptions,
|
||||||
|
) => {
|
||||||
|
switch (options?.finalizerExecutionMode ?? "sync") {
|
||||||
|
case "sync":
|
||||||
|
Runtime.runSync(runtime)(Scope.close(scope, Exit.void))
|
||||||
|
break
|
||||||
|
case "fork":
|
||||||
|
Runtime.runFork(runtime)(Scope.close(scope, Exit.void))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
export const useCallbackSync: {
|
export const useCallbackSync: {
|
||||||
<Args extends unknown[], A, E, R>(
|
<Args extends unknown[], A, E, R>(
|
||||||
callback: (...args: Args) => Effect.Effect<A, E, R>,
|
callback: (...args: Args) => Effect.Effect<A, E, R>,
|
||||||
@@ -57,16 +104,6 @@ export const useOnce: {
|
|||||||
return yield* useMemo(factory, [])
|
return yield* useMemo(factory, [])
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useMemoLayer: {
|
|
||||||
<ROut, E, RIn>(
|
|
||||||
layer: Layer.Layer<ROut, E, RIn>
|
|
||||||
): Effect.Effect<Context.Context<ROut>, E, RIn>
|
|
||||||
} = Effect.fn("useMemoLayer")(function* <ROut, E, RIn>(
|
|
||||||
layer: Layer.Layer<ROut, E, RIn>
|
|
||||||
) {
|
|
||||||
return yield* useMemo(() => Effect.provide(Effect.context<ROut>(), layer), [layer])
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
export const useEffect: {
|
export const useEffect: {
|
||||||
<E, R>(
|
<E, R>(
|
||||||
@@ -81,24 +118,14 @@ export const useEffect: {
|
|||||||
) {
|
) {
|
||||||
const runtime = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
const runtime = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => Effect.Do.pipe(
|
||||||
const { scope, exit } = Effect.Do.pipe(
|
|
||||||
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
||||||
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
||||||
|
Effect.map(({ scope }) =>
|
||||||
|
() => closeScope(scope, runtime, options)
|
||||||
|
),
|
||||||
Runtime.runSync(runtime),
|
Runtime.runSync(runtime),
|
||||||
)
|
), deps)
|
||||||
|
|
||||||
return () => {
|
|
||||||
switch (options?.finalizerExecutionMode ?? "sync") {
|
|
||||||
case "sync":
|
|
||||||
Runtime.runSync(runtime)(Scope.close(scope, exit))
|
|
||||||
break
|
|
||||||
case "fork":
|
|
||||||
Runtime.runFork(runtime)(Scope.close(scope, exit))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, deps)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useLayoutEffect: {
|
export const useLayoutEffect: {
|
||||||
@@ -114,24 +141,14 @@ export const useLayoutEffect: {
|
|||||||
) {
|
) {
|
||||||
const runtime = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
const runtime = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
||||||
|
|
||||||
React.useLayoutEffect(() => {
|
React.useLayoutEffect(() => Effect.Do.pipe(
|
||||||
const { scope, exit } = Effect.Do.pipe(
|
|
||||||
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
||||||
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
||||||
|
Effect.map(({ scope }) =>
|
||||||
|
() => closeScope(scope, runtime, options)
|
||||||
|
),
|
||||||
Runtime.runSync(runtime),
|
Runtime.runSync(runtime),
|
||||||
)
|
), deps)
|
||||||
|
|
||||||
return () => {
|
|
||||||
switch (options?.finalizerExecutionMode ?? "sync") {
|
|
||||||
case "sync":
|
|
||||||
Runtime.runSync(runtime)(Scope.close(scope, exit))
|
|
||||||
break
|
|
||||||
case "fork":
|
|
||||||
Runtime.runFork(runtime)(Scope.close(scope, exit))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, deps)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useFork: {
|
export const useFork: {
|
||||||
@@ -153,21 +170,30 @@ export const useFork: {
|
|||||||
: Scope.make(options?.finalizerExecutionStrategy)
|
: Scope.make(options?.finalizerExecutionStrategy)
|
||||||
)
|
)
|
||||||
Runtime.runFork(runtime)(Effect.provideService(effect(), Scope.Scope, scope), { ...options, scope })
|
Runtime.runFork(runtime)(Effect.provideService(effect(), Scope.Scope, scope), { ...options, scope })
|
||||||
|
return () => closeScope(scope, runtime, options)
|
||||||
return () => {
|
|
||||||
switch (options?.finalizerExecutionMode ?? "fork") {
|
|
||||||
case "sync":
|
|
||||||
Runtime.runSync(runtime)(Scope.close(scope, Exit.void))
|
|
||||||
break
|
|
||||||
case "fork":
|
|
||||||
Runtime.runFork(runtime)(Scope.close(scope, Exit.void))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, deps)
|
}, deps)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
export const useContextSync: {
|
||||||
|
<ROut, E, RIn>(
|
||||||
|
layer: Layer.Layer<ROut, E, RIn>,
|
||||||
|
options?: ScopeOptions,
|
||||||
|
): Effect.Effect<Context.Context<ROut>, E, Exclude<RIn, Scope.Scope>>
|
||||||
|
} = Effect.fn("useContextSync")(function* <ROut, E, RIn>(
|
||||||
|
layer: Layer.Layer<ROut, E, RIn>,
|
||||||
|
options?: ScopeOptions,
|
||||||
|
) {
|
||||||
|
const scope = yield* useScope([layer], options)
|
||||||
|
|
||||||
|
return yield* useMemo(() => Effect.provideService(
|
||||||
|
Effect.provide(Effect.context<ROut>(), layer),
|
||||||
|
Scope.Scope,
|
||||||
|
scope,
|
||||||
|
), [scope])
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
export const useRefFromReactiveValue: {
|
export const useRefFromReactiveValue: {
|
||||||
<A>(value: A): Effect.Effect<SubscriptionRef.SubscriptionRef<A>>
|
<A>(value: A): Effect.Effect<SubscriptionRef.SubscriptionRef<A>>
|
||||||
} = Effect.fn("useRefFromReactiveValue")(function*(value) {
|
} = Effect.fn("useRefFromReactiveValue")(function*(value) {
|
||||||
|
|||||||
@@ -10,12 +10,8 @@ const TodosStateLive = TodosState.Default("todos")
|
|||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: Component.make(function* Index() {
|
component: Component.make(function* Index() {
|
||||||
return yield* Effect.provide(
|
const context = yield* Hook.useContextSync(TodosStateLive, { finalizerExecutionMode: "fork" })
|
||||||
Component.use(Todos, Todos => <Todos />),
|
return yield* Effect.provide(Component.use(Todos, Todos => <Todos />), context)
|
||||||
yield* Hook.useMemoLayer(TodosStateLive),
|
|
||||||
)
|
|
||||||
}, {
|
|
||||||
finalizerExecutionMode: "fork"
|
|
||||||
}).pipe(
|
}).pipe(
|
||||||
Component.withRuntime(runtime.context),
|
Component.withRuntime(runtime.context),
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user