Last active
April 4, 2021 14:06
-
-
Save mikearnaldi/9a0f02d5634aee7b653eab99a9c6d660 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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