Reffuse context refactoring
All checks were successful
Lint / lint (push) Successful in 12s

This commit is contained in:
Julien Valverdé
2025-02-26 13:40:52 +01:00
parent 5f60d03d83
commit 693c7b2db8

View File

@@ -4,29 +4,9 @@ import * as ReffuseRuntime from "./ReffuseRuntime.js"
export class ReffuseContext<R> { export class ReffuseContext<R> {
readonly Context = React.createContext<Context.Context<R>>(null!) readonly Context = React.createContext<Context.Context<R>>(null!)
readonly Provider: ReffuseContextReactProvider<R> readonly Provider = makeProvider(this.Context)
readonly AsyncProvider = makeAsyncProvider(this.Context)
constructor() {
// TODO: scope the layer creation
this.Provider = props => {
const runtime = ReffuseRuntime.useRuntime()
const value = React.useMemo(() => Effect.context<R>().pipe(
Effect.provide(props.layer),
Runtime.runSync(runtime),
), [props.layer, runtime])
return (
<this.Context
{...props}
value={value}
/>
)
}
this.Provider.displayName = "ReffuseContextReactProvider"
}
useContext(): Context.Context<R> { useContext(): Context.Context<R> {
@@ -37,15 +17,73 @@ export class ReffuseContext<R> {
const context = this.useContext() const context = this.useContext()
return React.useMemo(() => Layer.effectContext(Effect.succeed(context)), [context]) return React.useMemo(() => Layer.effectContext(Effect.succeed(context)), [context])
} }
} }
export type ReffuseContextReactProvider<R> = React.FC<{ export type R<T> = T extends ReffuseContext<infer R> ? R : never
export type ReactProvider<R> = React.FC<{
readonly layer: Layer.Layer<R, unknown> readonly layer: Layer.Layer<R, unknown>
readonly children?: React.ReactNode readonly children?: React.ReactNode
}> }>
export type R<T> = T extends ReffuseContext<infer R> ? R : never function makeProvider<R>(Context: React.Context<Context.Context<R>>): ReactProvider<R> {
return function ReffuseContextReactProvider(props) {
const runtime = ReffuseRuntime.useRuntime()
const value = React.useMemo(() => Effect.context<R>().pipe(
Effect.provide(props.layer),
Runtime.runSync(runtime),
), [props.layer, runtime])
return (
<Context
{...props}
value={value}
/>
)
}
}
export type AsyncReactProvider<R> = React.FC<{
readonly layer: Layer.Layer<R, unknown>
readonly fallback?: React.ReactNode
readonly children?: React.ReactNode
}>
function makeAsyncProvider<R>(Context: React.Context<Context.Context<R>>): AsyncReactProvider<R> {
function Inner({ promise, children }: {
readonly promise: Promise<Context.Context<R>>
readonly children?: React.ReactNode
}) {
const value = React.use(promise)
return (
<Context
value={value}
children={children}
/>
)
}
return function ReffuseContextAsyncReactProvider(props) {
const runtime = ReffuseRuntime.useRuntime()
const promise = React.useMemo(() => Effect.context<R>().pipe(
Effect.provide(props.layer),
Runtime.runPromise(runtime),
), [props.layer, runtime])
return (
<React.Suspense fallback={props.fallback}>
<Inner
{...props}
promise={promise}
/>
</React.Suspense>
)
}
}
export function make<R = never>() { export function make<R = never>() {