@@ -1,5 +1,6 @@
|
|||||||
import { Context, Effect, Fiber, FiberRefs, Layer, ManagedRuntime, Ref, Runtime, RuntimeFlags, Scope, Stream, SubscriptionRef } from "effect"
|
import { Context, Effect, Fiber, Layer, ManagedRuntime, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
|
import * as ReffuseReactContext from "./ReffuseReactContext.js"
|
||||||
|
|
||||||
|
|
||||||
export interface ProviderProps<R, E> {
|
export interface ProviderProps<R, E> {
|
||||||
@@ -10,31 +11,71 @@ export interface ProviderProps<R, E> {
|
|||||||
|
|
||||||
export class Reffuse<R> {
|
export class Reffuse<R> {
|
||||||
|
|
||||||
readonly Context = React.createContext<Runtime.Runtime<R>>(null!)
|
readonly Context = React.createContext<ReffuseReactContext.ReffuseReactContext<R>>(null!)
|
||||||
readonly Provider: React.FC<ProviderProps<R, any>>
|
readonly Provider: React.FC<ProviderProps<R, unknown>>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
runtime: Runtime.Runtime<R>
|
runtime: Runtime.Runtime<R>
|
||||||
) {
|
) {
|
||||||
this.Provider = (props: ProviderProps<R, any>) => {
|
this.Provider = (props: ProviderProps<R, unknown>) => {
|
||||||
const runtime = React.useMemo(() => Runtime.make({
|
const value = React.useMemo(() => ({
|
||||||
context: Context.empty(),
|
runtime,
|
||||||
runtimeFlags: RuntimeFlags.make(),
|
context: Effect.context<R>().pipe(
|
||||||
fiberRefs: FiberRefs.empty(),
|
Effect.provide(props.layer),
|
||||||
}), [])
|
Runtime.runSync(runtime),
|
||||||
|
),
|
||||||
|
}), [props.layer])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<this.Context
|
<this.Context
|
||||||
{...props}
|
{...props}
|
||||||
value={runtime}
|
value={value}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useRuntime(): Runtime.Runtime<R> {
|
useRuntime(): Runtime.Runtime<never> {
|
||||||
return React.useContext(this.Context)
|
return React.useContext(this.Context).runtime
|
||||||
|
}
|
||||||
|
|
||||||
|
useContext(): Context.Context<R> {
|
||||||
|
return React.useContext(this.Context).context
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
useRunSync() {
|
||||||
|
const { runtime, context } = React.useContext(this.Context)
|
||||||
|
|
||||||
|
return React.useCallback(<A, E>(effect: Effect.Effect<A, E, R>): A => effect.pipe(
|
||||||
|
Effect.provide(context),
|
||||||
|
Runtime.runSync(runtime),
|
||||||
|
), [runtime, context])
|
||||||
|
}
|
||||||
|
|
||||||
|
useRunPromise() {
|
||||||
|
const { runtime, context } = React.useContext(this.Context)
|
||||||
|
|
||||||
|
return React.useCallback(<A, E>(
|
||||||
|
effect: Effect.Effect<A, E, R>,
|
||||||
|
options?: { readonly signal?: AbortSignal },
|
||||||
|
): Promise<A> => effect.pipe(
|
||||||
|
Effect.provide(context),
|
||||||
|
effect => Runtime.runPromise(runtime)(effect, options),
|
||||||
|
), [runtime, context])
|
||||||
|
}
|
||||||
|
|
||||||
|
useRunFork() {
|
||||||
|
const { runtime, context } = React.useContext(this.Context)
|
||||||
|
|
||||||
|
return React.useCallback(<A, E>(
|
||||||
|
effect: Effect.Effect<A, E, R>,
|
||||||
|
options?: Runtime.RunForkOptions,
|
||||||
|
): Fiber.RuntimeFiber<A, E> => effect.pipe(
|
||||||
|
Effect.provide(context),
|
||||||
|
effect => Runtime.runFork(runtime)(effect, options),
|
||||||
|
), [runtime, context])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -43,32 +84,32 @@ export class Reffuse<R> {
|
|||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: Runtime.RunForkOptions,
|
options?: Runtime.RunForkOptions,
|
||||||
): void {
|
): void {
|
||||||
const runtime = this.useRuntime()
|
const runFork = this.useRunFork()
|
||||||
|
|
||||||
return React.useEffect(() => {
|
return React.useEffect(() => {
|
||||||
const fiber = runtime.runFork(Effect.scoped(self), options)
|
const fiber = runFork(Effect.scoped(self), options)
|
||||||
return () => { runtime.runFork(Fiber.interrupt(fiber)) }
|
return () => { runFork(Fiber.interrupt(fiber)) }
|
||||||
}, [runtime, ...deps ?? []])
|
}, [runFork, ...deps ?? []])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useRef<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
|
useRef<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
|
||||||
const runtime = this.useRuntime()
|
const runSync = this.useRunSync()
|
||||||
return React.useMemo(() => runtime.runSync(SubscriptionRef.make(value)), [])
|
return React.useMemo(() => runSync(SubscriptionRef.make(value)), [])
|
||||||
}
|
}
|
||||||
|
|
||||||
useRefFromEffect<A, E>(effect: Effect.Effect<A, E, R>): SubscriptionRef.SubscriptionRef<A> {
|
useRefFromEffect<A, E>(effect: Effect.Effect<A, E, R>): SubscriptionRef.SubscriptionRef<A> {
|
||||||
const runtime = this.useRuntime()
|
const runSync = this.useRunSync()
|
||||||
|
|
||||||
return React.useMemo(() => runtime.runSync(effect.pipe(
|
return React.useMemo(() => runSync(effect.pipe(
|
||||||
Effect.flatMap(SubscriptionRef.make)
|
Effect.flatMap(SubscriptionRef.make)
|
||||||
)), [])
|
)), [])
|
||||||
}
|
}
|
||||||
|
|
||||||
useRefState<A>(ref: SubscriptionRef.SubscriptionRef<A>): [A, React.Dispatch<React.SetStateAction<A>>] {
|
useRefState<A>(ref: SubscriptionRef.SubscriptionRef<A>): [A, React.Dispatch<React.SetStateAction<A>>] {
|
||||||
const runtime = this.useRuntime()
|
const runSync = this.useRunSync()
|
||||||
|
|
||||||
const initialState = React.useMemo(() => runtime.runSync(ref), [ref])
|
const initialState = React.useMemo(() => runSync(ref), [ref])
|
||||||
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
||||||
|
|
||||||
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
||||||
@@ -76,7 +117,7 @@ export class Reffuse<R> {
|
|||||||
)), [ref])
|
)), [ref])
|
||||||
|
|
||||||
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
||||||
runtime.runSync(Ref.update(ref, previousState =>
|
runSync(Ref.update(ref, previousState =>
|
||||||
typeof setStateAction === "function"
|
typeof setStateAction === "function"
|
||||||
? (setStateAction as (prevState: A) => A)(previousState)
|
? (setStateAction as (prevState: A) => A)(previousState)
|
||||||
: setStateAction
|
: setStateAction
|
||||||
@@ -89,5 +130,5 @@ export class Reffuse<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const make = <ROut, E>(layer: Layer.Layer<ROut, E, never>): Reffuse<ROut, E> =>
|
export const make = <R>(): Reffuse<R> =>
|
||||||
new Reffuse(ManagedRuntime.make(layer))
|
new Reffuse(ManagedRuntime.make(layer))
|
||||||
|
|||||||
7
packages/reffuse/src/ReffuseReactContext.ts
Normal file
7
packages/reffuse/src/ReffuseReactContext.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import type { Context, Runtime } from "effect"
|
||||||
|
|
||||||
|
|
||||||
|
export interface ReffuseReactContext<R> {
|
||||||
|
readonly runtime: Runtime.Runtime<never>
|
||||||
|
readonly context: Context.Context<R>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user