0.1.1 #3
@@ -8,7 +8,7 @@
|
||||
"clean:node": "rm -rf node_modules"
|
||||
},
|
||||
"devDependencies": {
|
||||
"npm-check-updates": "^17.1.13",
|
||||
"npm-check-updates": "^17.1.14",
|
||||
"npm-sort": "^0.0.4",
|
||||
"typescript": "^5.7.3"
|
||||
}
|
||||
|
||||
@@ -11,31 +11,32 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@tanstack/react-router": "^1.95.3",
|
||||
"@tanstack/router-devtools": "^1.95.3",
|
||||
"@tanstack/router-plugin": "^1.95.3",
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@tanstack/react-router": "^1.97.3",
|
||||
"@tanstack/router-devtools": "^1.97.3",
|
||||
"@tanstack/router-plugin": "^1.97.3",
|
||||
"@thilawyn/thilaschema": "^0.1.4",
|
||||
"@types/react": "^19.0.4",
|
||||
"@types/react-dom": "^19.0.2",
|
||||
"@types/react": "^19.0.7",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@vitejs/plugin-react": "^4.3.4",
|
||||
"effect": "^3.12.1",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint-plugin-react-hooks": "^5.0.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.16",
|
||||
"effect": "^3.12.5",
|
||||
"eslint": "^9.18.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.18",
|
||||
"globals": "^15.14.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"reffuse": "workspace:*",
|
||||
"typescript-eslint": "^8.18.2",
|
||||
"vite": "^6.0.5"
|
||||
"typescript-eslint": "^8.21.0",
|
||||
"vite": "^6.0.11"
|
||||
},
|
||||
"dependencies": {
|
||||
"@effect/platform": "^0.73.1",
|
||||
"@effect/platform-browser": "^0.52.1",
|
||||
"@effect/platform": "^0.74.0",
|
||||
"@effect/platform-browser": "^0.53.0",
|
||||
"@radix-ui/themes": "^3.1.6",
|
||||
"@typed/id": "^0.17.1",
|
||||
"lucide-react": "^0.471.1",
|
||||
"@typed/lazy-ref": "^0.3.3",
|
||||
"lucide-react": "^0.473.0",
|
||||
"mobx": "^6.13.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { FetchHttpClient } from "@effect/platform"
|
||||
import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser"
|
||||
import { createRouter, RouterProvider } from "@tanstack/react-router"
|
||||
import { ReffuseRuntime } from "@thilawyn/reffuse"
|
||||
import { Layer } from "effect"
|
||||
import { StrictMode } from "react"
|
||||
import { createRoot } from "react-dom/client"
|
||||
import { ReffuseRuntime } from "reffuse"
|
||||
import { GlobalContext } from "./reffuse"
|
||||
import { routeTree } from "./routeTree.gen"
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@
|
||||
|
||||
import { Route as rootRoute } from './routes/__root'
|
||||
import { Route as TimeImport } from './routes/time'
|
||||
import { Route as TestsImport } from './routes/tests'
|
||||
import { Route as CountImport } from './routes/count'
|
||||
import { Route as BlankImport } from './routes/blank'
|
||||
import { Route as IndexImport } from './routes/index'
|
||||
|
||||
// Create/Update Routes
|
||||
@@ -23,12 +25,24 @@ const TimeRoute = TimeImport.update({
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const TestsRoute = TestsImport.update({
|
||||
id: '/tests',
|
||||
path: '/tests',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const CountRoute = CountImport.update({
|
||||
id: '/count',
|
||||
path: '/count',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const BlankRoute = BlankImport.update({
|
||||
id: '/blank',
|
||||
path: '/blank',
|
||||
getParentRoute: () => rootRoute,
|
||||
} as any)
|
||||
|
||||
const IndexRoute = IndexImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
@@ -46,6 +60,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof IndexImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/blank': {
|
||||
id: '/blank'
|
||||
path: '/blank'
|
||||
fullPath: '/blank'
|
||||
preLoaderRoute: typeof BlankImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/count': {
|
||||
id: '/count'
|
||||
path: '/count'
|
||||
@@ -53,6 +74,13 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof CountImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/tests': {
|
||||
id: '/tests'
|
||||
path: '/tests'
|
||||
fullPath: '/tests'
|
||||
preLoaderRoute: typeof TestsImport
|
||||
parentRoute: typeof rootRoute
|
||||
}
|
||||
'/time': {
|
||||
id: '/time'
|
||||
path: '/time'
|
||||
@@ -67,41 +95,51 @@ declare module '@tanstack/react-router' {
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/blank': typeof BlankRoute
|
||||
'/count': typeof CountRoute
|
||||
'/tests': typeof TestsRoute
|
||||
'/time': typeof TimeRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/blank': typeof BlankRoute
|
||||
'/count': typeof CountRoute
|
||||
'/tests': typeof TestsRoute
|
||||
'/time': typeof TimeRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/blank': typeof BlankRoute
|
||||
'/count': typeof CountRoute
|
||||
'/tests': typeof TestsRoute
|
||||
'/time': typeof TimeRoute
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/' | '/count' | '/time'
|
||||
fullPaths: '/' | '/blank' | '/count' | '/tests' | '/time'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/' | '/count' | '/time'
|
||||
id: '__root__' | '/' | '/count' | '/time'
|
||||
to: '/' | '/blank' | '/count' | '/tests' | '/time'
|
||||
id: '__root__' | '/' | '/blank' | '/count' | '/tests' | '/time'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
BlankRoute: typeof BlankRoute
|
||||
CountRoute: typeof CountRoute
|
||||
TestsRoute: typeof TestsRoute
|
||||
TimeRoute: typeof TimeRoute
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
BlankRoute: BlankRoute,
|
||||
CountRoute: CountRoute,
|
||||
TestsRoute: TestsRoute,
|
||||
TimeRoute: TimeRoute,
|
||||
}
|
||||
|
||||
@@ -116,16 +154,24 @@ export const routeTree = rootRoute
|
||||
"filePath": "__root.tsx",
|
||||
"children": [
|
||||
"/",
|
||||
"/blank",
|
||||
"/count",
|
||||
"/tests",
|
||||
"/time"
|
||||
]
|
||||
},
|
||||
"/": {
|
||||
"filePath": "index.tsx"
|
||||
},
|
||||
"/blank": {
|
||||
"filePath": "blank.tsx"
|
||||
},
|
||||
"/count": {
|
||||
"filePath": "count.tsx"
|
||||
},
|
||||
"/tests": {
|
||||
"filePath": "tests.tsx"
|
||||
},
|
||||
"/time": {
|
||||
"filePath": "time.tsx"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Container, Flex, Theme } from "@radix-ui/themes"
|
||||
import "@radix-ui/themes/styles.css"
|
||||
import { createRootRoute, Link, Outlet } from "@tanstack/react-router"
|
||||
import { TanStackRouterDevtools } from "@tanstack/router-devtools"
|
||||
|
||||
import "@radix-ui/themes/styles.css"
|
||||
import "../index.css"
|
||||
|
||||
|
||||
@@ -17,6 +18,8 @@ function Root() {
|
||||
<Link to="/">Index</Link>
|
||||
<Link to="/time">Time</Link>
|
||||
<Link to="/count">Count</Link>
|
||||
<Link to="/tests">Tests</Link>
|
||||
<Link to="/blank">Blank</Link>
|
||||
</Flex>
|
||||
</Container>
|
||||
|
||||
|
||||
9
packages/example/src/routes/blank.tsx
Normal file
9
packages/example/src/routes/blank.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/blank')({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/blank"!</div>
|
||||
}
|
||||
@@ -1,10 +1,9 @@
|
||||
import { R } from "@/reffuse"
|
||||
import { TodosContext } from "@/todos/reffuse"
|
||||
import { TodosState } from "@/todos/services"
|
||||
import { VTodos } from "@/todos/views/VTodos"
|
||||
import { Container } from "@radix-ui/themes"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { Console, Effect, Layer } from "effect"
|
||||
import { Layer } from "effect"
|
||||
import { useMemo } from "react"
|
||||
|
||||
|
||||
@@ -18,10 +17,6 @@ function Index() {
|
||||
Layer.provideMerge(TodosState.make("todos"))
|
||||
), [])
|
||||
|
||||
R.useEffect(Effect.addFinalizer(() => Console.log("Effect cleanup")).pipe(
|
||||
Effect.flatMap(() => Console.log("Effect recalculated"))
|
||||
))
|
||||
|
||||
|
||||
return (
|
||||
<Container>
|
||||
|
||||
23
packages/example/src/routes/tests.tsx
Normal file
23
packages/example/src/routes/tests.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import { R } from "@/reffuse"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { GetRandomValues, makeUuid4 } from "@typed/id"
|
||||
import { Console, Effect } from "effect"
|
||||
|
||||
|
||||
export const Route = createFileRoute("/tests")({
|
||||
component: RouteComponent
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
// R.useMemo(Effect.addFinalizer(() => Console.log("Cleanup!")).pipe(
|
||||
// Effect.map(() => "test")
|
||||
// ))
|
||||
|
||||
const value = R.useMemoScoped(Effect.addFinalizer(() => Console.log("cleanup")).pipe(
|
||||
Effect.andThen(makeUuid4),
|
||||
Effect.provide(GetRandomValues.CryptoRandom),
|
||||
), [])
|
||||
console.log(value)
|
||||
|
||||
return <div>Hello "/tests"!</div>
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { R } from "@/reffuse"
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { Console, DateTime, Effect, Ref, Schedule, Stream } from "effect"
|
||||
import { DateTime, Ref, Schedule, Stream } from "effect"
|
||||
|
||||
|
||||
const timeEverySecond = Stream.repeatEffectWithSchedule(
|
||||
@@ -16,21 +16,7 @@ export const Route = createFileRoute("/time")({
|
||||
function Time() {
|
||||
|
||||
const timeRef = R.useRefFromEffect(DateTime.now)
|
||||
|
||||
R.useFork(Effect.addFinalizer(() => Console.log("Cleanup")).pipe(
|
||||
Effect.flatMap(() =>
|
||||
Stream.runForEach(timeEverySecond, v => Ref.set(timeRef, v))
|
||||
)
|
||||
), [timeRef])
|
||||
// Reffuse.useFork(Effect.addFinalizer(() => Console.log("Cleanup")).pipe(
|
||||
// Effect.flatMap(() => DateTime.now),
|
||||
// Effect.flatMap(v => Ref.set(timeRef, v)),
|
||||
// Effect.repeat(Schedule.intersect(
|
||||
// Schedule.forever,
|
||||
// Schedule.spaced("1 second"),
|
||||
// )),
|
||||
// ), [timeRef])
|
||||
|
||||
R.useFork(Stream.runForEach(timeEverySecond, v => Ref.set(timeRef, v)), [timeRef])
|
||||
const [time] = R.useRefState(timeRef)
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "reffuse",
|
||||
"version": "0.1.0",
|
||||
"version": "0.1.1",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"./README.md",
|
||||
@@ -29,8 +29,9 @@
|
||||
"clean:node": "rm -rf node_modules"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.0.4",
|
||||
"effect": "^3.12.1",
|
||||
"@typed/lazy-ref": "^0.3.3",
|
||||
"@types/react": "^19.0.7",
|
||||
"effect": "^3.12.5",
|
||||
"react": "^19.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Context, Effect, ExecutionStrategy, Exit, Fiber, Option, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
|
||||
import * as LazyRef from "@typed/lazy-ref"
|
||||
import { Context, Effect, ExecutionStrategy, Exit, Fiber, Ref, Runtime, Scope, Stream, SubscriptionRef } from "effect"
|
||||
import React from "react"
|
||||
import * as ReffuseContext from "./ReffuseContext.js"
|
||||
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
||||
@@ -55,6 +56,19 @@ export class Reffuse<R> {
|
||||
), [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`.
|
||||
@@ -78,6 +92,55 @@ export class Reffuse<R> {
|
||||
])
|
||||
}
|
||||
|
||||
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 closeInitialScope = Scope.close(initialScope, Exit.void).pipe(
|
||||
Effect.andThen(Effect.sync(() => { initialScopeClosed.current = true })),
|
||||
Effect.when(() => !initialScopeClosed.current),
|
||||
)
|
||||
|
||||
const [scope, value] = closeInitialScope.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`.
|
||||
*
|
||||
@@ -171,20 +234,6 @@ export class Reffuse<R> {
|
||||
])
|
||||
}
|
||||
|
||||
useSuspense<A, E>(
|
||||
effect: Effect.Effect<A, E, R>,
|
||||
deps?: React.DependencyList,
|
||||
options?: { readonly signal?: AbortSignal } & RenderOptions,
|
||||
): A {
|
||||
const runPromise = this.useRunPromise()
|
||||
|
||||
const promise = React.useMemo(() => runPromise(effect, options), [
|
||||
...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runPromise],
|
||||
...(deps ?? []),
|
||||
])
|
||||
return React.use(promise)
|
||||
}
|
||||
|
||||
/**
|
||||
* An asynchronous and non-blocking alternative to `React.useEffect`.
|
||||
*
|
||||
@@ -226,7 +275,7 @@ export class Reffuse<R> {
|
||||
|
||||
return () => {
|
||||
Fiber.interrupt(fiber).pipe(
|
||||
Effect.flatMap(() => Scope.close(scope, Exit.void)),
|
||||
Effect.andThen(Scope.close(scope, Exit.void)),
|
||||
runFork,
|
||||
)
|
||||
}
|
||||
@@ -236,6 +285,44 @@ export class Reffuse<R> {
|
||||
])
|
||||
}
|
||||
|
||||
// useSuspense<A, E>(
|
||||
// effect: Effect.Effect<A, E, R>,
|
||||
// deps?: React.DependencyList,
|
||||
// options?: { readonly signal?: AbortSignal } & RenderOptions,
|
||||
// ): A {
|
||||
// const runPromise = this.useRunPromise()
|
||||
|
||||
// const promise = React.useMemo(() => runPromise(effect, options), [
|
||||
// ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runPromise],
|
||||
// ...(deps ?? []),
|
||||
// ])
|
||||
// return React.use(promise)
|
||||
// }
|
||||
|
||||
// useSuspenseScoped<A, E>(
|
||||
// effect: Effect.Effect<A, E, R | Scope.Scope>,
|
||||
// deps?: React.DependencyList,
|
||||
// options?: { readonly signal?: AbortSignal } & RenderOptions & ScopeOptions,
|
||||
// ): A {
|
||||
// const runSync = this.useRunSync()
|
||||
// const runPromise = this.useRunPromise()
|
||||
|
||||
// const initialPromise = React.useMemo(() => runPromise(Effect.scoped(effect)), [])
|
||||
// const [promise, setPromise] = React.useState(initialPromise)
|
||||
|
||||
// React.useEffect(() => {
|
||||
// const scope = runSync(Scope.make())
|
||||
// setPromise(runPromise(Effect.provideService(effect, Scope.Scope, scope), options))
|
||||
|
||||
// return () => { runPromise(Scope.close(scope, Exit.void)) }
|
||||
// }, [
|
||||
// ...options?.doNotReExecuteOnRuntimeOrContextChange ? [] : [runSync, runPromise],
|
||||
// ...(deps ?? []),
|
||||
// ])
|
||||
|
||||
// return React.use(promise)
|
||||
// }
|
||||
|
||||
|
||||
useRef<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
|
||||
return this.useMemo(
|
||||
@@ -256,14 +343,14 @@ export class Reffuse<R> {
|
||||
/**
|
||||
* 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 of the ref changes.
|
||||
* 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), [ref])
|
||||
const initialState = React.useMemo(() => runSync(ref), [])
|
||||
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
||||
|
||||
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
||||
@@ -271,23 +358,38 @@ export class Reffuse<R> {
|
||||
)), [ref])
|
||||
|
||||
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
||||
runSync(Ref.update(ref, previousState =>
|
||||
SetStateAction.value(setStateAction, previousState)
|
||||
runSync(Ref.update(ref, prevState =>
|
||||
SetStateAction.value(setStateAction, prevState)
|
||||
)),
|
||||
[ref])
|
||||
|
||||
return [reactStateValue, setValue]
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the state of a `LazyRef` from the `@typed/lazy-ref` package 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.
|
||||
*/
|
||||
useLazyRefState<A, E>(ref: LazyRef.LazyRef<A, E, R>): [A, React.Dispatch<React.SetStateAction<A>>] {
|
||||
const runSync = this.useRunSync()
|
||||
|
||||
useStreamState<A, E>(stream: Stream.Stream<A, E, R>): Option.Option<A> {
|
||||
const [reactStateValue, setReactStateValue] = React.useState(Option.none<A>())
|
||||
const initialState = React.useMemo(() => runSync(ref), [])
|
||||
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
||||
|
||||
this.useFork(Stream.runForEach(stream, v => Effect.sync(() =>
|
||||
setReactStateValue(Option.some(v))
|
||||
)), [stream])
|
||||
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
||||
setReactStateValue(v)
|
||||
)), [ref])
|
||||
|
||||
return reactStateValue
|
||||
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
||||
runSync(LazyRef.update(ref, prevState =>
|
||||
SetStateAction.value(setStateAction, prevState)
|
||||
)),
|
||||
[ref])
|
||||
|
||||
return [reactStateValue, setValue]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ export class ReffuseContext<R> {
|
||||
readonly Provider: ReffuseContextReactProvider<R>
|
||||
|
||||
constructor() {
|
||||
// TODO: scope the layer creation
|
||||
this.Provider = (props) => {
|
||||
const runtime = ReffuseRuntime.useRuntime()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user