0.1.1 (#3)
Co-authored-by: Julien Valverdé <julien.valverde@mailo.com> Reviewed-on: https://gitea:3000/Thilawyn/reffuse/pulls/3
This commit was merged in pull request #3.
This commit is contained in:
@@ -8,7 +8,7 @@
|
|||||||
"clean:node": "rm -rf node_modules"
|
"clean:node": "rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"npm-check-updates": "^17.1.13",
|
"npm-check-updates": "^17.1.14",
|
||||||
"npm-sort": "^0.0.4",
|
"npm-sort": "^0.0.4",
|
||||||
"typescript": "^5.7.3"
|
"typescript": "^5.7.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,31 +11,32 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.18.0",
|
||||||
"@tanstack/react-router": "^1.95.3",
|
"@tanstack/react-router": "^1.97.3",
|
||||||
"@tanstack/router-devtools": "^1.95.3",
|
"@tanstack/router-devtools": "^1.97.3",
|
||||||
"@tanstack/router-plugin": "^1.95.3",
|
"@tanstack/router-plugin": "^1.97.3",
|
||||||
"@thilawyn/thilaschema": "^0.1.4",
|
"@thilawyn/thilaschema": "^0.1.4",
|
||||||
"@types/react": "^19.0.4",
|
"@types/react": "^19.0.7",
|
||||||
"@types/react-dom": "^19.0.2",
|
"@types/react-dom": "^19.0.3",
|
||||||
"@vitejs/plugin-react": "^4.3.4",
|
"@vitejs/plugin-react": "^4.3.4",
|
||||||
"effect": "^3.12.1",
|
"effect": "^3.12.5",
|
||||||
"eslint": "^9.17.0",
|
"eslint": "^9.18.0",
|
||||||
"eslint-plugin-react-hooks": "^5.0.0",
|
"eslint-plugin-react-hooks": "^5.1.0",
|
||||||
"eslint-plugin-react-refresh": "^0.4.16",
|
"eslint-plugin-react-refresh": "^0.4.18",
|
||||||
"globals": "^15.14.0",
|
"globals": "^15.14.0",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"reffuse": "workspace:*",
|
"reffuse": "workspace:*",
|
||||||
"typescript-eslint": "^8.18.2",
|
"typescript-eslint": "^8.21.0",
|
||||||
"vite": "^6.0.5"
|
"vite": "^6.0.11"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@effect/platform": "^0.73.1",
|
"@effect/platform": "^0.74.0",
|
||||||
"@effect/platform-browser": "^0.52.1",
|
"@effect/platform-browser": "^0.53.0",
|
||||||
"@radix-ui/themes": "^3.1.6",
|
"@radix-ui/themes": "^3.1.6",
|
||||||
"@typed/id": "^0.17.1",
|
"@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"
|
"mobx": "^6.13.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { FetchHttpClient } from "@effect/platform"
|
import { FetchHttpClient } from "@effect/platform"
|
||||||
import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser"
|
import { Clipboard, Geolocation, Permissions } from "@effect/platform-browser"
|
||||||
import { createRouter, RouterProvider } from "@tanstack/react-router"
|
import { createRouter, RouterProvider } from "@tanstack/react-router"
|
||||||
import { ReffuseRuntime } from "@thilawyn/reffuse"
|
|
||||||
import { Layer } from "effect"
|
import { Layer } from "effect"
|
||||||
import { StrictMode } from "react"
|
import { StrictMode } from "react"
|
||||||
import { createRoot } from "react-dom/client"
|
import { createRoot } from "react-dom/client"
|
||||||
|
import { ReffuseRuntime } from "reffuse"
|
||||||
import { GlobalContext } from "./reffuse"
|
import { GlobalContext } from "./reffuse"
|
||||||
import { routeTree } from "./routeTree.gen"
|
import { routeTree } from "./routeTree.gen"
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
import { Route as rootRoute } from './routes/__root'
|
import { Route as rootRoute } from './routes/__root'
|
||||||
import { Route as TimeImport } from './routes/time'
|
import { Route as TimeImport } from './routes/time'
|
||||||
|
import { Route as TestsImport } from './routes/tests'
|
||||||
import { Route as CountImport } from './routes/count'
|
import { Route as CountImport } from './routes/count'
|
||||||
|
import { Route as BlankImport } from './routes/blank'
|
||||||
import { Route as IndexImport } from './routes/index'
|
import { Route as IndexImport } from './routes/index'
|
||||||
|
|
||||||
// Create/Update Routes
|
// Create/Update Routes
|
||||||
@@ -23,12 +25,24 @@ const TimeRoute = TimeImport.update({
|
|||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const TestsRoute = TestsImport.update({
|
||||||
|
id: '/tests',
|
||||||
|
path: '/tests',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const CountRoute = CountImport.update({
|
const CountRoute = CountImport.update({
|
||||||
id: '/count',
|
id: '/count',
|
||||||
path: '/count',
|
path: '/count',
|
||||||
getParentRoute: () => rootRoute,
|
getParentRoute: () => rootRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
|
||||||
|
const BlankRoute = BlankImport.update({
|
||||||
|
id: '/blank',
|
||||||
|
path: '/blank',
|
||||||
|
getParentRoute: () => rootRoute,
|
||||||
|
} as any)
|
||||||
|
|
||||||
const IndexRoute = IndexImport.update({
|
const IndexRoute = IndexImport.update({
|
||||||
id: '/',
|
id: '/',
|
||||||
path: '/',
|
path: '/',
|
||||||
@@ -46,6 +60,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof IndexImport
|
preLoaderRoute: typeof IndexImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/blank': {
|
||||||
|
id: '/blank'
|
||||||
|
path: '/blank'
|
||||||
|
fullPath: '/blank'
|
||||||
|
preLoaderRoute: typeof BlankImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/count': {
|
'/count': {
|
||||||
id: '/count'
|
id: '/count'
|
||||||
path: '/count'
|
path: '/count'
|
||||||
@@ -53,6 +74,13 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof CountImport
|
preLoaderRoute: typeof CountImport
|
||||||
parentRoute: typeof rootRoute
|
parentRoute: typeof rootRoute
|
||||||
}
|
}
|
||||||
|
'/tests': {
|
||||||
|
id: '/tests'
|
||||||
|
path: '/tests'
|
||||||
|
fullPath: '/tests'
|
||||||
|
preLoaderRoute: typeof TestsImport
|
||||||
|
parentRoute: typeof rootRoute
|
||||||
|
}
|
||||||
'/time': {
|
'/time': {
|
||||||
id: '/time'
|
id: '/time'
|
||||||
path: '/time'
|
path: '/time'
|
||||||
@@ -67,41 +95,51 @@ declare module '@tanstack/react-router' {
|
|||||||
|
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
|
'/blank': typeof BlankRoute
|
||||||
'/count': typeof CountRoute
|
'/count': typeof CountRoute
|
||||||
|
'/tests': typeof TestsRoute
|
||||||
'/time': typeof TimeRoute
|
'/time': typeof TimeRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
|
'/blank': typeof BlankRoute
|
||||||
'/count': typeof CountRoute
|
'/count': typeof CountRoute
|
||||||
|
'/tests': typeof TestsRoute
|
||||||
'/time': typeof TimeRoute
|
'/time': typeof TimeRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRoute
|
__root__: typeof rootRoute
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
|
'/blank': typeof BlankRoute
|
||||||
'/count': typeof CountRoute
|
'/count': typeof CountRoute
|
||||||
|
'/tests': typeof TestsRoute
|
||||||
'/time': typeof TimeRoute
|
'/time': typeof TimeRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
fullPaths: '/' | '/count' | '/time'
|
fullPaths: '/' | '/blank' | '/count' | '/tests' | '/time'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to: '/' | '/count' | '/time'
|
to: '/' | '/blank' | '/count' | '/tests' | '/time'
|
||||||
id: '__root__' | '/' | '/count' | '/time'
|
id: '__root__' | '/' | '/blank' | '/count' | '/tests' | '/time'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
|
BlankRoute: typeof BlankRoute
|
||||||
CountRoute: typeof CountRoute
|
CountRoute: typeof CountRoute
|
||||||
|
TestsRoute: typeof TestsRoute
|
||||||
TimeRoute: typeof TimeRoute
|
TimeRoute: typeof TimeRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootRouteChildren: RootRouteChildren = {
|
const rootRouteChildren: RootRouteChildren = {
|
||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
|
BlankRoute: BlankRoute,
|
||||||
CountRoute: CountRoute,
|
CountRoute: CountRoute,
|
||||||
|
TestsRoute: TestsRoute,
|
||||||
TimeRoute: TimeRoute,
|
TimeRoute: TimeRoute,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,16 +154,24 @@ export const routeTree = rootRoute
|
|||||||
"filePath": "__root.tsx",
|
"filePath": "__root.tsx",
|
||||||
"children": [
|
"children": [
|
||||||
"/",
|
"/",
|
||||||
|
"/blank",
|
||||||
"/count",
|
"/count",
|
||||||
|
"/tests",
|
||||||
"/time"
|
"/time"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"/": {
|
"/": {
|
||||||
"filePath": "index.tsx"
|
"filePath": "index.tsx"
|
||||||
},
|
},
|
||||||
|
"/blank": {
|
||||||
|
"filePath": "blank.tsx"
|
||||||
|
},
|
||||||
"/count": {
|
"/count": {
|
||||||
"filePath": "count.tsx"
|
"filePath": "count.tsx"
|
||||||
},
|
},
|
||||||
|
"/tests": {
|
||||||
|
"filePath": "tests.tsx"
|
||||||
|
},
|
||||||
"/time": {
|
"/time": {
|
||||||
"filePath": "time.tsx"
|
"filePath": "time.tsx"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Container, Flex, Theme } from "@radix-ui/themes"
|
import { Container, Flex, Theme } from "@radix-ui/themes"
|
||||||
import "@radix-ui/themes/styles.css"
|
|
||||||
import { createRootRoute, Link, Outlet } from "@tanstack/react-router"
|
import { createRootRoute, Link, Outlet } from "@tanstack/react-router"
|
||||||
import { TanStackRouterDevtools } from "@tanstack/router-devtools"
|
import { TanStackRouterDevtools } from "@tanstack/router-devtools"
|
||||||
|
|
||||||
|
import "@radix-ui/themes/styles.css"
|
||||||
import "../index.css"
|
import "../index.css"
|
||||||
|
|
||||||
|
|
||||||
@@ -17,6 +18,8 @@ function Root() {
|
|||||||
<Link to="/">Index</Link>
|
<Link to="/">Index</Link>
|
||||||
<Link to="/time">Time</Link>
|
<Link to="/time">Time</Link>
|
||||||
<Link to="/count">Count</Link>
|
<Link to="/count">Count</Link>
|
||||||
|
<Link to="/tests">Tests</Link>
|
||||||
|
<Link to="/blank">Blank</Link>
|
||||||
</Flex>
|
</Flex>
|
||||||
</Container>
|
</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 { TodosContext } from "@/todos/reffuse"
|
||||||
import { TodosState } from "@/todos/services"
|
import { TodosState } from "@/todos/services"
|
||||||
import { VTodos } from "@/todos/views/VTodos"
|
import { VTodos } from "@/todos/views/VTodos"
|
||||||
import { Container } from "@radix-ui/themes"
|
import { Container } from "@radix-ui/themes"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { Console, Effect, Layer } from "effect"
|
import { Layer } from "effect"
|
||||||
import { useMemo } from "react"
|
import { useMemo } from "react"
|
||||||
|
|
||||||
|
|
||||||
@@ -18,10 +17,6 @@ function Index() {
|
|||||||
Layer.provideMerge(TodosState.make("todos"))
|
Layer.provideMerge(TodosState.make("todos"))
|
||||||
), [])
|
), [])
|
||||||
|
|
||||||
R.useEffect(Effect.addFinalizer(() => Console.log("Effect cleanup")).pipe(
|
|
||||||
Effect.flatMap(() => Console.log("Effect recalculated"))
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<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 { R } from "@/reffuse"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
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(
|
const timeEverySecond = Stream.repeatEffectWithSchedule(
|
||||||
@@ -16,21 +16,7 @@ export const Route = createFileRoute("/time")({
|
|||||||
function Time() {
|
function Time() {
|
||||||
|
|
||||||
const timeRef = R.useRefFromEffect(DateTime.now)
|
const timeRef = R.useRefFromEffect(DateTime.now)
|
||||||
|
R.useFork(Stream.runForEach(timeEverySecond, v => Ref.set(timeRef, v)), [timeRef])
|
||||||
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])
|
|
||||||
|
|
||||||
const [time] = R.useRefState(timeRef)
|
const [time] = R.useRefState(timeRef)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "reffuse",
|
"name": "reffuse",
|
||||||
"version": "0.1.0",
|
"version": "0.1.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"./README.md",
|
"./README.md",
|
||||||
@@ -29,8 +29,9 @@
|
|||||||
"clean:node": "rm -rf node_modules"
|
"clean:node": "rm -rf node_modules"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^19.0.4",
|
"@typed/lazy-ref": "^0.3.3",
|
||||||
"effect": "^3.12.1",
|
"@types/react": "^19.0.7",
|
||||||
|
"effect": "^3.12.5",
|
||||||
"react": "^19.0.0"
|
"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 React from "react"
|
||||||
import * as ReffuseContext from "./ReffuseContext.js"
|
import * as ReffuseContext from "./ReffuseContext.js"
|
||||||
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
import * as ReffuseRuntime from "./ReffuseRuntime.js"
|
||||||
@@ -55,6 +56,19 @@ export class Reffuse<R> {
|
|||||||
), [runtime, context])
|
), [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`.
|
* 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`.
|
* 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`.
|
* An asynchronous and non-blocking alternative to `React.useEffect`.
|
||||||
*
|
*
|
||||||
@@ -226,7 +275,7 @@ export class Reffuse<R> {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
Fiber.interrupt(fiber).pipe(
|
Fiber.interrupt(fiber).pipe(
|
||||||
Effect.flatMap(() => Scope.close(scope, Exit.void)),
|
Effect.andThen(Scope.close(scope, Exit.void)),
|
||||||
runFork,
|
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> {
|
useRef<A>(value: A): SubscriptionRef.SubscriptionRef<A> {
|
||||||
return this.useMemo(
|
return this.useMemo(
|
||||||
@@ -256,14 +343,14 @@ export class Reffuse<R> {
|
|||||||
/**
|
/**
|
||||||
* Binds the state of a `SubscriptionRef` to the state of the React component.
|
* 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.
|
* 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>(ref: SubscriptionRef.SubscriptionRef<A>): [A, React.Dispatch<React.SetStateAction<A>>] {
|
||||||
const runSync = this.useRunSync()
|
const runSync = this.useRunSync()
|
||||||
|
|
||||||
const initialState = React.useMemo(() => runSync(ref), [ref])
|
const initialState = React.useMemo(() => runSync(ref), [])
|
||||||
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
||||||
|
|
||||||
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
||||||
@@ -271,23 +358,38 @@ export class Reffuse<R> {
|
|||||||
)), [ref])
|
)), [ref])
|
||||||
|
|
||||||
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
const setValue = React.useCallback((setStateAction: React.SetStateAction<A>) =>
|
||||||
runSync(Ref.update(ref, previousState =>
|
runSync(Ref.update(ref, prevState =>
|
||||||
SetStateAction.value(setStateAction, previousState)
|
SetStateAction.value(setStateAction, prevState)
|
||||||
)),
|
)),
|
||||||
[ref])
|
[ref])
|
||||||
|
|
||||||
return [reactStateValue, setValue]
|
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 initialState = React.useMemo(() => runSync(ref), [])
|
||||||
const [reactStateValue, setReactStateValue] = React.useState(Option.none<A>())
|
const [reactStateValue, setReactStateValue] = React.useState(initialState)
|
||||||
|
|
||||||
this.useFork(Stream.runForEach(stream, v => Effect.sync(() =>
|
this.useFork(Stream.runForEach(ref.changes, v => Effect.sync(() =>
|
||||||
setReactStateValue(Option.some(v))
|
setReactStateValue(v)
|
||||||
)), [stream])
|
)), [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>
|
readonly Provider: ReffuseContextReactProvider<R>
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
// TODO: scope the layer creation
|
||||||
this.Provider = (props) => {
|
this.Provider = (props) => {
|
||||||
const runtime = ReffuseRuntime.useRuntime()
|
const runtime = ReffuseRuntime.useRuntime()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user