0.1.11 #14
@@ -1,399 +1,29 @@
|
|||||||
import { Context, Effect, ExecutionStrategy, Exit, Fiber, Pipeable, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
|
|
||||||
import * as React from "react"
|
|
||||||
import * as ReffuseContext from "./ReffuseContext.js"
|
import * as ReffuseContext from "./ReffuseContext.js"
|
||||||
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
import * as ReffuseHelpers from "./ReffuseHelpers.js"
|
||||||
import * as SetStateAction from "./SetStateAction.js"
|
import type { Merge, StaticType } from "./types.js"
|
||||||
|
|
||||||
|
|
||||||
export class Reffuse<R> {
|
export class Reffuse extends ReffuseHelpers.make() {}
|
||||||
|
|
||||||
constructor(
|
export const withContexts = <R2 extends Array<unknown>>(
|
||||||
readonly contexts: readonly ReffuseContext.ReffuseContext<R>[]
|
...contexts: [...{ [K in keyof R2]: ReffuseContext.ReffuseContext<R2[K]> }]
|
||||||
) {}
|
) =>
|
||||||
|
<
|
||||||
|
BaseClass extends ReffuseHelpers.ReffuseHelpersClass<R1>,
|
||||||
useContext(): Context.Context<R> {
|
R1
|
||||||
return ReffuseContext.useMergeAll(...this.contexts)
|
>(
|
||||||
}
|
self: BaseClass & ReffuseHelpers.ReffuseHelpersClass<R1>
|
||||||
|
): (
|
||||||
|
{
|
||||||
useRunSync() {
|
new(): Merge<
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
InstanceType<BaseClass>,
|
||||||
const context = this.useContext()
|
{ constructor: ReffuseHelpers.ReffuseHelpersClass<R1 | R2[number]> }
|
||||||
|
>
|
||||||
return React.useCallback(<A, E>(
|
} &
|
||||||
effect: Effect.Effect<A, E, R>
|
Merge<
|
||||||
): A => effect.pipe(
|
StaticType<BaseClass>,
|
||||||
Effect.provide(context),
|
StaticType<ReffuseHelpers.ReffuseHelpersClass<R1 | R2[number]>>
|
||||||
Runtime.runSync(runtime),
|
>
|
||||||
), [runtime, context])
|
) => class extends self {
|
||||||
}
|
readonly contexts = [...self.contexts, ...contexts]
|
||||||
|
} as any
|
||||||
useRunPromise() {
|
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
|
||||||
const context = this.useContext()
|
|
||||||
|
|
||||||
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 = ReffuseRuntime.useRuntime()
|
|
||||||
const context = this.useContext()
|
|
||||||
|
|
||||||
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])
|
|
||||||
}
|
|
||||||
|
|
||||||
useRunCallback() {
|
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
|
||||||
const context = this.useContext()
|
|
||||||
|
|
||||||
return React.useCallback(<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R>,
|
|
||||||
options?: Runtime.RunCallbackOptions<A, E>,
|
|
||||||
): Runtime.Cancel<A, E> => effect.pipe(
|
|
||||||
Effect.provide(context),
|
|
||||||
effect => Runtime.runCallback(runtime)(effect, options),
|
|
||||||
), [runtime, context])
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reffuse equivalent to `React.useMemo`.
|
|
||||||
*
|
|
||||||
* `useMemo` will only recompute the memoized value by running the given synchronous effect when one of the deps has changed. \
|
|
||||||
* Trying to run an asynchronous effect will throw.
|
|
||||||
*
|
|
||||||
* Changes to the Reffuse runtime or context will recompute the value in addition to the deps.
|
|
||||||
* You can disable this behavior by setting `doNotReExecuteOnRuntimeOrContextChange` to `true` in `options`.
|
|
||||||
*/
|
|
||||||
useMemo<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R>,
|
|
||||||
deps?: React.DependencyList,
|
|
||||||
options?: RenderOptions,
|
|
||||||
): A {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
|
|
||||||
return React.useMemo(() => runSync(effect), [
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync],
|
|
||||||
...(deps ?? []),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
useMemoScoped<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
|
||||||
deps?: React.DependencyList,
|
|
||||||
options?: RenderOptions & ScopeOptions,
|
|
||||||
): A {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
|
|
||||||
// Calculate an initial version of the value so that it can be accessed during the first render
|
|
||||||
const [initialScope, initialValue] = React.useMemo(() => Scope.make(options?.finalizerExecutionStrategy).pipe(
|
|
||||||
Effect.flatMap(scope => effect.pipe(
|
|
||||||
Effect.provideService(Scope.Scope, scope),
|
|
||||||
Effect.map(value => [scope, value] as const),
|
|
||||||
)),
|
|
||||||
|
|
||||||
runSync,
|
|
||||||
), [])
|
|
||||||
|
|
||||||
// Keep track of the state of the initial scope
|
|
||||||
const initialScopeClosed = React.useRef(false)
|
|
||||||
|
|
||||||
const [value, setValue] = React.useState(initialValue)
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const closeInitialScopeIfNeeded = Scope.close(initialScope, Exit.void).pipe(
|
|
||||||
Effect.andThen(Effect.sync(() => { initialScopeClosed.current = true })),
|
|
||||||
Effect.when(() => !initialScopeClosed.current),
|
|
||||||
)
|
|
||||||
|
|
||||||
const [scope, value] = closeInitialScopeIfNeeded.pipe(
|
|
||||||
Effect.andThen(Scope.make(options?.finalizerExecutionStrategy).pipe(
|
|
||||||
Effect.flatMap(scope => effect.pipe(
|
|
||||||
Effect.provideService(Scope.Scope, scope),
|
|
||||||
Effect.map(value => [scope, value] as const),
|
|
||||||
))
|
|
||||||
)),
|
|
||||||
|
|
||||||
runSync,
|
|
||||||
)
|
|
||||||
|
|
||||||
setValue(value)
|
|
||||||
return () => { runSync(Scope.close(scope, Exit.void)) }
|
|
||||||
}, [
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync],
|
|
||||||
...(deps ?? []),
|
|
||||||
])
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reffuse equivalent to `React.useEffect`.
|
|
||||||
*
|
|
||||||
* Executes a synchronous effect wrapped into a Scope when one of the deps has changed. Trying to run an asynchronous effect will throw.
|
|
||||||
*
|
|
||||||
* The Scope is closed on every cleanup, i.e. when one of the deps has changed and the effect needs to be re-executed. \
|
|
||||||
* Add finalizers to the Scope to handle cleanup logic.
|
|
||||||
*
|
|
||||||
* Changes to the Reffuse runtime or context will re-execute the effect in addition to the deps.
|
|
||||||
* You can disable this behavior by setting `doNotReExecuteOnRuntimeOrContextChange` to `true` in `options`.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
* ```
|
|
||||||
* useEffect(Effect.addFinalizer(() => Console.log("Component unmounted")).pipe(
|
|
||||||
* Effect.flatMap(() => Console.log("Component mounted"))
|
|
||||||
* ))
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Plain React equivalent:
|
|
||||||
* ```
|
|
||||||
* React.useEffect(() => {
|
|
||||||
* console.log("Component mounted")
|
|
||||||
* return () => { console.log("Component unmounted") }
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
useEffect<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
|
||||||
deps?: React.DependencyList,
|
|
||||||
options?: RenderOptions & ScopeOptions,
|
|
||||||
): void {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
|
|
||||||
return React.useEffect(() => {
|
|
||||||
const scope = Scope.make(options?.finalizerExecutionStrategy).pipe(
|
|
||||||
Effect.tap(scope => Effect.provideService(effect, Scope.Scope, scope)),
|
|
||||||
runSync,
|
|
||||||
)
|
|
||||||
|
|
||||||
return () => { runSync(Scope.close(scope, Exit.void)) }
|
|
||||||
}, [
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync],
|
|
||||||
...(deps ?? []),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reffuse equivalent to `React.useLayoutEffect`.
|
|
||||||
*
|
|
||||||
* Executes a synchronous effect wrapped into a Scope when one of the deps has changed. Fires synchronously after all DOM mutations. \
|
|
||||||
* Trying to run an asynchronous effect will throw.
|
|
||||||
*
|
|
||||||
* The Scope is closed on every cleanup, i.e. when one of the deps has changed and the effect needs to be re-executed. \
|
|
||||||
* Add finalizers to the Scope to handle cleanup logic.
|
|
||||||
*
|
|
||||||
* Changes to the Reffuse runtime or context will re-execute the effect in addition to the deps.
|
|
||||||
* You can disable this behavior by setting `doNotReExecuteOnRuntimeOrContextChange` to `true` in `options`.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
* ```
|
|
||||||
* useLayoutEffect(Effect.addFinalizer(() => Console.log("Component unmounted")).pipe(
|
|
||||||
* Effect.flatMap(() => Console.log("Component mounted"))
|
|
||||||
* ))
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Plain React equivalent:
|
|
||||||
* ```
|
|
||||||
* React.useLayoutEffect(() => {
|
|
||||||
* console.log("Component mounted")
|
|
||||||
* return () => { console.log("Component unmounted") }
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
useLayoutEffect<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
|
||||||
deps?: React.DependencyList,
|
|
||||||
options?: RenderOptions & ScopeOptions,
|
|
||||||
): void {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
|
|
||||||
return React.useLayoutEffect(() => {
|
|
||||||
const scope = Scope.make(options?.finalizerExecutionStrategy).pipe(
|
|
||||||
Effect.tap(scope => Effect.provideService(effect, Scope.Scope, scope)),
|
|
||||||
runSync,
|
|
||||||
)
|
|
||||||
|
|
||||||
return () => { runSync(Scope.close(scope, Exit.void)) }
|
|
||||||
}, [
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync],
|
|
||||||
...(deps ?? []),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An asynchronous and non-blocking alternative to `React.useEffect`.
|
|
||||||
*
|
|
||||||
* Forks an effect wrapped into a Scope in the background when one of the deps has changed.
|
|
||||||
*
|
|
||||||
* The Scope is closed on every cleanup, i.e. when one of the deps has changed and the effect needs to be re-executed. \
|
|
||||||
* Add finalizers to the Scope to handle cleanup logic.
|
|
||||||
*
|
|
||||||
* Changes to the Reffuse runtime or context will re-execute the effect in addition to the deps.
|
|
||||||
* You can disable this behavior by setting `doNotReExecuteOnRuntimeOrContextChange` to `true` in `options`.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
* ```
|
|
||||||
* const timeRef = useRefFromEffect(DateTime.now)
|
|
||||||
*
|
|
||||||
* useFork(Effect.addFinalizer(() => Console.log("Cleanup")).pipe(
|
|
||||||
* Effect.map(() => Stream.repeatEffectWithSchedule(
|
|
||||||
* DateTime.now,
|
|
||||||
* Schedule.intersect(Schedule.forever, Schedule.spaced("1 second")),
|
|
||||||
* )),
|
|
||||||
*
|
|
||||||
* Effect.flatMap(Stream.runForEach(time => Ref.set(timeRef, time)),
|
|
||||||
* )), [timeRef])
|
|
||||||
*
|
|
||||||
* const [time] = useRefState(timeRef)
|
|
||||||
* ```
|
|
||||||
*/
|
|
||||||
useFork<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
|
||||||
deps?: React.DependencyList,
|
|
||||||
options?: Runtime.RunForkOptions & RenderOptions & ScopeOptions,
|
|
||||||
): void {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
const runFork = this.useRunFork()
|
|
||||||
|
|
||||||
return React.useEffect(() => {
|
|
||||||
const scope = runSync(options?.scope
|
|
||||||
? Scope.fork(options.scope, options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
|
||||||
: Scope.make(options?.finalizerExecutionStrategy)
|
|
||||||
)
|
|
||||||
runFork(Effect.provideService(effect, Scope.Scope, scope), { ...options, scope })
|
|
||||||
|
|
||||||
return () => { runFork(Scope.close(scope, Exit.void)) }
|
|
||||||
}, [
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runFork],
|
|
||||||
...(deps ?? []),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
usePromise<A, E>(
|
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
|
||||||
deps?: React.DependencyList,
|
|
||||||
options?: { readonly signal?: AbortSignal } & Runtime.RunForkOptions & RenderOptions & ScopeOptions,
|
|
||||||
): Promise<A> {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
const runFork = this.useRunFork()
|
|
||||||
|
|
||||||
const [value, setValue] = React.useState(Promise.withResolvers<A>().promise)
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const { promise, resolve, reject } = Promise.withResolvers<A>()
|
|
||||||
setValue(promise)
|
|
||||||
|
|
||||||
const scope = runSync(options?.scope
|
|
||||||
? Scope.fork(options.scope, options?.finalizerExecutionStrategy ?? ExecutionStrategy.sequential)
|
|
||||||
: Scope.make(options?.finalizerExecutionStrategy)
|
|
||||||
)
|
|
||||||
|
|
||||||
const cleanup = () => { runFork(Scope.close(scope, Exit.void)) }
|
|
||||||
if (options?.signal)
|
|
||||||
options.signal.addEventListener("abort", cleanup)
|
|
||||||
|
|
||||||
effect.pipe(
|
|
||||||
Effect.provideService(Scope.Scope, scope),
|
|
||||||
Effect.match({
|
|
||||||
onSuccess: resolve,
|
|
||||||
onFailure: reject,
|
|
||||||
}),
|
|
||||||
effect => runFork(effect, { ...options, scope }),
|
|
||||||
)
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
if (options?.signal)
|
|
||||||
options.signal.removeEventListener("abort", cleanup)
|
|
||||||
|
|
||||||
cleanup()
|
|
||||||
}
|
|
||||||
}, [
|
|
||||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runFork],
|
|
||||||
...(deps ?? []),
|
|
||||||
])
|
|
||||||
|
|
||||||
return value
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
useRef<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
|
|
||||||
return this.useMemo(
|
|
||||||
SubscriptionRef.make(value),
|
|
||||||
[],
|
|
||||||
{ doNotReExecuteOnRuntimeOrContextChange: true }, // Do not recreate the ref when the context changes
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Binds the state of a `SubscriptionRef` to the state of the React component.
|
|
||||||
*
|
|
||||||
* Returns a [value, setter] tuple just like `React.useState` and triggers a re-render everytime the value held by the ref changes.
|
|
||||||
*
|
|
||||||
* Note that the rules of React's immutable state still apply: updating a ref with the same value will not trigger a re-render.
|
|
||||||
*/
|
|
||||||
useRefState<A>(ref: SubscriptionRef.SubscriptionRef<A>): [A, React.Dispatch<React.SetStateAction<A>>] {
|
|
||||||
const runSync = this.useRunSync()
|
|
||||||
|
|
||||||
const initialState = React.useMemo(() => runSync(ref), [])
|
|
||||||
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
|
||||||
|
|
||||||
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
|
||||||
setReactStateValue(v)
|
|
||||||
)), [ref])
|
|
||||||
|
|
||||||
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
|
||||||
runSync(Ref.update(ref, prevState =>
|
|
||||||
SetStateAction.value(setStateAction, prevState)
|
|
||||||
)),
|
|
||||||
[ref])
|
|
||||||
|
|
||||||
return [reactStateValue, setValue]
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface Reffuse<R> extends Pipeable.Pipeable {}
|
|
||||||
|
|
||||||
Reffuse.prototype.pipe = function pipe() {
|
|
||||||
return Pipeable.pipeArguments(this, arguments)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface RenderOptions {
|
|
||||||
/** Prevents re-executing the effect when the Effect runtime or context changes. Defaults to `false`. */
|
|
||||||
readonly doNotReExecuteOnRuntimeOrContextChange?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ScopeOptions {
|
|
||||||
readonly finalizerExecutionStrategy?: ExecutionStrategy.ExecutionStrategy
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const make = <T extends Array<unknown>>(
|
|
||||||
...contexts: [...{ [K in keyof T]: ReffuseContext.ReffuseContext<T[K]> }]
|
|
||||||
): Reffuse<T[number]> =>
|
|
||||||
new Reffuse(contexts)
|
|
||||||
|
|
||||||
// export const make = (): Reffuse<never> => new Reffuse([])
|
|
||||||
|
|
||||||
// export const withContexts = <R2 extends Array<unknown>>(
|
|
||||||
// ...contexts: [...{ [K in keyof R2]: ReffuseContext.ReffuseContext<R2[K]> }]
|
|
||||||
// ) =>
|
|
||||||
// <T extends Reffuse<R1>, R1>(self: T & Reffuse<R1>): (
|
|
||||||
// Reffuse<R1 | R2[number]> & Exclude<T, Reffuse<R1>>
|
|
||||||
// ) =>
|
|
||||||
// new Reffuse([...self.contexts, ...contexts as any]) as any
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
import { Effect } from "effect"
|
import { Effect } from "effect"
|
||||||
|
import * as Reffuse from "./Reffuse.js"
|
||||||
import * as ReffuseContext from "./ReffuseContext.js"
|
import * as ReffuseContext from "./ReffuseContext.js"
|
||||||
import * as ReffuseHelpers from "./ReffuseHelpers.js"
|
import * as ReffuseHelpers from "./ReffuseHelpers.js"
|
||||||
import type { Merge, StaticType } from "./types.js"
|
import type { Merge, StaticType } from "./types.js"
|
||||||
|
|
||||||
|
|
||||||
class Reffuse extends ReffuseHelpers.make([]) {}
|
|
||||||
|
|
||||||
class MyService extends Effect.Service<MyService>()("MyService", {
|
class MyService extends Effect.Service<MyService>()("MyService", {
|
||||||
succeed: {}
|
succeed: {}
|
||||||
}) {}
|
}) {}
|
||||||
@@ -28,32 +27,9 @@ const make = <Ext extends object>(extension: Ext) =>
|
|||||||
return class_ as any
|
return class_ as any
|
||||||
}
|
}
|
||||||
|
|
||||||
export const withContexts = <R2 extends Array<unknown>>(
|
|
||||||
...contexts: [...{ [K in keyof R2]: ReffuseContext.ReffuseContext<R2[K]> }]
|
|
||||||
) =>
|
|
||||||
<
|
|
||||||
BaseClass extends ReffuseHelpers.ReffuseHelpersClass<R1>,
|
|
||||||
R1
|
|
||||||
>(
|
|
||||||
self: BaseClass & ReffuseHelpers.ReffuseHelpersClass<R1>
|
|
||||||
): (
|
|
||||||
{
|
|
||||||
new(): Merge<
|
|
||||||
InstanceType<BaseClass>,
|
|
||||||
ReffuseHelpers.ReffuseHelpers<R1 | R2[number]>
|
|
||||||
>
|
|
||||||
} &
|
|
||||||
Merge<
|
|
||||||
StaticType<BaseClass>,
|
|
||||||
StaticType<ReffuseHelpers.ReffuseHelpersClass<R1 | R2[number]>>
|
|
||||||
>
|
|
||||||
) => class extends self {
|
|
||||||
readonly contexts = [...self.contexts, ...contexts]
|
|
||||||
} as any
|
|
||||||
|
|
||||||
|
const withMyContext = Reffuse.withContexts(MyContext)
|
||||||
const withMyContext = withContexts(MyContext)
|
const clsWithMyContext = withMyContext(Reffuse.Reffuse)
|
||||||
const clsWithMyContext = withMyContext(Reffuse)
|
|
||||||
class ReffuseWithMyContext extends clsWithMyContext {}
|
class ReffuseWithMyContext extends clsWithMyContext {}
|
||||||
|
|
||||||
|
|
||||||
@@ -61,9 +37,9 @@ const withProut = make({
|
|||||||
prout<R>(this: ReffuseHelpers.ReffuseHelpers<R>) {}
|
prout<R>(this: ReffuseHelpers.ReffuseHelpers<R>) {}
|
||||||
})
|
})
|
||||||
|
|
||||||
class MyReffuse extends Reffuse.pipe(
|
class MyReffuse extends Reffuse.Reffuse.pipe(
|
||||||
withProut,
|
withProut,
|
||||||
withContexts(MyContext),
|
Reffuse.withContexts(MyContext),
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
new MyReffuse().useFork()
|
new MyReffuse().useFork()
|
||||||
|
|||||||
@@ -16,16 +16,15 @@ export interface ScopeOptions {
|
|||||||
|
|
||||||
|
|
||||||
export abstract class ReffuseHelpers<R> {
|
export abstract class ReffuseHelpers<R> {
|
||||||
|
|
||||||
declare ["constructor"]: ReffuseHelpersClass<R>
|
declare ["constructor"]: ReffuseHelpersClass<R>
|
||||||
|
|
||||||
|
|
||||||
useContext(): Context.Context<R> {
|
useContext<R>(this: ReffuseHelpers<R>): Context.Context<R> {
|
||||||
return ReffuseContext.useMergeAll(...this.constructor.contexts)
|
return ReffuseContext.useMergeAll(...this.constructor.contexts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useRunSync() {
|
useRunSync<R>(this: ReffuseHelpers<R>) {
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
const runtime = ReffuseRuntime.useRuntime()
|
||||||
const context = this.useContext()
|
const context = this.useContext()
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
), [runtime, context])
|
), [runtime, context])
|
||||||
}
|
}
|
||||||
|
|
||||||
useRunPromise() {
|
useRunPromise<R>(this: ReffuseHelpers<R>) {
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
const runtime = ReffuseRuntime.useRuntime()
|
||||||
const context = this.useContext()
|
const context = this.useContext()
|
||||||
|
|
||||||
@@ -50,7 +49,7 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
), [runtime, context])
|
), [runtime, context])
|
||||||
}
|
}
|
||||||
|
|
||||||
useRunFork() {
|
useRunFork<R>(this: ReffuseHelpers<R>) {
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
const runtime = ReffuseRuntime.useRuntime()
|
||||||
const context = this.useContext()
|
const context = this.useContext()
|
||||||
|
|
||||||
@@ -63,7 +62,7 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
), [runtime, context])
|
), [runtime, context])
|
||||||
}
|
}
|
||||||
|
|
||||||
useRunCallback() {
|
useRunCallback<R>(this: ReffuseHelpers<R>) {
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
const runtime = ReffuseRuntime.useRuntime()
|
||||||
const context = this.useContext()
|
const context = this.useContext()
|
||||||
|
|
||||||
@@ -86,7 +85,8 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
* Changes to the Reffuse runtime or context will recompute the value in addition to the deps.
|
* Changes to the Reffuse runtime or context will recompute the value in addition to the deps.
|
||||||
* You can disable this behavior by setting `doNotReExecuteOnRuntimeOrContextChange` to `true` in `options`.
|
* You can disable this behavior by setting `doNotReExecuteOnRuntimeOrContextChange` to `true` in `options`.
|
||||||
*/
|
*/
|
||||||
useMemo<A, E>(
|
useMemo<A, E, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
effect: Effect.Effect<A, E, R>,
|
effect: Effect.Effect<A, E, R>,
|
||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: RenderOptions,
|
options?: RenderOptions,
|
||||||
@@ -99,7 +99,8 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
useMemoScoped<A, E>(
|
useMemoScoped<A, E, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: RenderOptions & ScopeOptions,
|
options?: RenderOptions & ScopeOptions,
|
||||||
@@ -174,7 +175,8 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
useEffect<A, E>(
|
useEffect<A, E, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: RenderOptions & ScopeOptions,
|
options?: RenderOptions & ScopeOptions,
|
||||||
@@ -221,7 +223,8 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
useLayoutEffect<A, E>(
|
useLayoutEffect<A, E, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: RenderOptions & ScopeOptions,
|
options?: RenderOptions & ScopeOptions,
|
||||||
@@ -268,7 +271,8 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
* const [time] = useRefState(timeRef)
|
* const [time] = useRefState(timeRef)
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
useFork<A, E>(
|
useFork<A, E, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: Runtime.RunForkOptions & RenderOptions & ScopeOptions,
|
options?: Runtime.RunForkOptions & RenderOptions & ScopeOptions,
|
||||||
@@ -290,7 +294,8 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
usePromise<A, E>(
|
usePromise<A, E, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
effect: Effect.Effect<A, E, R | Scope.Scope>,
|
||||||
deps?: React.DependencyList,
|
deps?: React.DependencyList,
|
||||||
options?: { readonly signal?: AbortSignal } & Runtime.RunForkOptions & RenderOptions & ScopeOptions,
|
options?: { readonly signal?: AbortSignal } & Runtime.RunForkOptions & RenderOptions & ScopeOptions,
|
||||||
@@ -337,7 +342,10 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
useRef<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
|
useRef<A, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
|
value: A,
|
||||||
|
): SubscriptionRef.SubscriptionRef<A> {
|
||||||
return this.useMemo(
|
return this.useMemo(
|
||||||
SubscriptionRef.make(value),
|
SubscriptionRef.make(value),
|
||||||
[],
|
[],
|
||||||
@@ -352,7 +360,10 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
*
|
*
|
||||||
* Note that the rules of React's immutable state still apply: updating a ref with the same value will not trigger a re-render.
|
* Note that the rules of React's immutable state still apply: updating a ref with the same value will not trigger a re-render.
|
||||||
*/
|
*/
|
||||||
useRefState<A>(ref: SubscriptionRef.SubscriptionRef<A>): [A, React.Dispatch<React.SetStateAction<A>>] {
|
useRefState<A, R>(
|
||||||
|
this: ReffuseHelpers<R>,
|
||||||
|
ref: SubscriptionRef.SubscriptionRef<A>,
|
||||||
|
): [A, React.Dispatch<React.SetStateAction<A>>] {
|
||||||
const runSync = this.useRunSync()
|
const runSync = this.useRunSync()
|
||||||
|
|
||||||
const initialState = React.useMemo(() => runSync(ref), [])
|
const initialState = React.useMemo(() => runSync(ref), [])
|
||||||
@@ -370,7 +381,6 @@ export abstract class ReffuseHelpers<R> {
|
|||||||
|
|
||||||
return [reactStateValue, setValue]
|
return [reactStateValue, setValue]
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -391,7 +401,7 @@ export interface ReffuseHelpersClass<R> extends Pipeable.Pipeable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const make = <R = never>(contexts: readonly ReffuseContext.ReffuseContext<R>[]): ReffuseHelpersClass<R> =>
|
export const make = (): ReffuseHelpersClass<never> =>
|
||||||
class extends (ReffuseHelpers<R> as ReffuseHelpersClass<R>) {
|
class extends (ReffuseHelpers<never> as ReffuseHelpersClass<never>) {
|
||||||
static readonly contexts = contexts
|
static readonly contexts = []
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user