Skip to content

Instantly share code, notes, and snippets.

@mikearnaldi
Last active April 4, 2021 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikearnaldi/9a0f02d5634aee7b653eab99a9c6d660 to your computer and use it in GitHub Desktop.
Save mikearnaldi/9a0f02d5634aee7b653eab99a9c6d660 to your computer and use it in GitHub Desktop.
import * as T from "@effect-ts/core/Effect"
import * as Ex from "@effect-ts/core/Effect/Exit"
import * as Fiber from "@effect-ts/core/Effect/Fiber"
import * as L from "@effect-ts/core/Effect/Layer"
import * as RM from "@effect-ts/core/Effect/Managed/ReleaseMap"
import * as P from "@effect-ts/core/Effect/Promise"
import * as Ref from "@effect-ts/core/Effect/Ref"
import * as React from "react"
export function makeLayerContext<R>(layer: L.Layer<T.DefaultEnv, never, R>) {
const LayerPromiseContext = React.createContext(
undefined as P.Promise<never, R & T.DefaultEnv> | undefined
)
function Provider({ children }: { children: JSX.Element }) {
const promise = React.useMemo(
() => P.unsafeMake<never, R & T.DefaultEnv>(Fiber.None),
[]
)
React.useEffect(() => {
const relMap = new RM.ReleaseMap(
Ref.unsafeMakeRef<RM.State>(new RM.Running(0, new Map()))
)
const effect = T.map_(
T.provideSome_(
L.build(layer["+++"](L.identity<T.DefaultEnv>())).effect,
(r: T.DefaultEnv) => [r, relMap] as const
),
([_, r]) => r
)
const cancel = T.runCancel(T.to_(effect, promise))
return () => {
T.run(cancel)
T.run(RM.releaseAll(Ex.interrupt(Fiber.None), T.sequential)(relMap))
}
}, [])
return (
<LayerPromiseContext.Provider value={promise}>
{children}
</LayerPromiseContext.Provider>
)
}
return {
useProvider: (): (<E, A>(
self: T.Effect<R & T.DefaultEnv, E, A>
) => T.Effect<unknown, E, A>) => {
const LayerPromise = React.useContext(LayerPromiseContext)
if (LayerPromise) {
return (self) =>
P.await(LayerPromise)["|>"](T.chain((r) => T.provideAll_(self, r)))
} else {
throw new Error("App should be wrapped in <ProvideLayer>APP</ProvideLayer>")
}
},
ProvideLayer: ({ children }: { children: JSX.Element }) => {
const LayerPromise = React.useContext(LayerPromiseContext)
if (LayerPromise) {
return (
<LayerPromiseContext.Provider value={LayerPromise}>
{children}
</LayerPromiseContext.Provider>
)
} else {
return <Provider>{children}</Provider>
}
},
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment