0.1.0 #1
@@ -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 Hook from "./Hook.js"
|
||||
|
||||
|
||||
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>>()
|
||||
|
||||
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 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: {
|
||||
<E, R, P extends {}>(
|
||||
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: {
|
||||
<Args extends unknown[], A, E, R>(
|
||||
callback: (...args: Args) => Effect.Effect<A, E, R>,
|
||||
@@ -57,16 +104,6 @@ export const useOnce: {
|
||||
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: {
|
||||
<E, R>(
|
||||
@@ -81,24 +118,14 @@ export const useEffect: {
|
||||
) {
|
||||
const runtime = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
||||
|
||||
React.useEffect(() => {
|
||||
const { scope, exit } = Effect.Do.pipe(
|
||||
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
||||
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
||||
Runtime.runSync(runtime),
|
||||
)
|
||||
|
||||
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)
|
||||
React.useEffect(() => Effect.Do.pipe(
|
||||
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
||||
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
||||
Effect.map(({ scope }) =>
|
||||
() => closeScope(scope, runtime, options)
|
||||
),
|
||||
Runtime.runSync(runtime),
|
||||
), deps)
|
||||
})
|
||||
|
||||
export const useLayoutEffect: {
|
||||
@@ -114,24 +141,14 @@ export const useLayoutEffect: {
|
||||
) {
|
||||
const runtime = yield* Effect.runtime<Exclude<R, Scope.Scope>>()
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
const { scope, exit } = Effect.Do.pipe(
|
||||
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
||||
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
||||
Runtime.runSync(runtime),
|
||||
)
|
||||
|
||||
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)
|
||||
React.useLayoutEffect(() => Effect.Do.pipe(
|
||||
Effect.bind("scope", () => Scope.make(options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)),
|
||||
Effect.bind("exit", ({ scope }) => Effect.exit(Effect.provideService(effect(), Scope.Scope, scope))),
|
||||
Effect.map(({ scope }) =>
|
||||
() => closeScope(scope, runtime, options)
|
||||
),
|
||||
Runtime.runSync(runtime),
|
||||
), deps)
|
||||
})
|
||||
|
||||
export const useFork: {
|
||||
@@ -153,21 +170,30 @@ export const useFork: {
|
||||
: Scope.make(options?.finalizerExecutionStrategy)
|
||||
)
|
||||
Runtime.runFork(runtime)(Effect.provideService(effect(), Scope.Scope, scope), { ...options, scope })
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
return () => closeScope(scope, runtime, options)
|
||||
}, 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: {
|
||||
<A>(value: A): Effect.Effect<SubscriptionRef.SubscriptionRef<A>>
|
||||
} = Effect.fn("useRefFromReactiveValue")(function*(value) {
|
||||
|
||||
@@ -10,12 +10,8 @@ const TodosStateLive = TodosState.Default("todos")
|
||||
|
||||
export const Route = createFileRoute("/")({
|
||||
component: Component.make(function* Index() {
|
||||
return yield* Effect.provide(
|
||||
Component.use(Todos, Todos => <Todos />),
|
||||
yield* Hook.useMemoLayer(TodosStateLive),
|
||||
)
|
||||
}, {
|
||||
finalizerExecutionMode: "fork"
|
||||
const context = yield* Hook.useContextSync(TodosStateLive, { finalizerExecutionMode: "fork" })
|
||||
return yield* Effect.provide(Component.use(Todos, Todos => <Todos />), context)
|
||||
}).pipe(
|
||||
Component.withRuntime(runtime.context),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user