useMemoScoped
All checks were successful
Lint / lint (push) Successful in 11s

This commit is contained in:
Julien Valverdé
2025-01-22 03:18:20 +01:00
parent 37cfcf6714
commit 22e5bbfcc2
4 changed files with 35 additions and 9 deletions

BIN
bun.lockb

Binary file not shown.

View File

@@ -35,6 +35,7 @@
"@effect/platform-browser": "^0.52.1",
"@radix-ui/themes": "^3.1.6",
"@typed/id": "^0.17.1",
"@typed/lazy-ref": "^0.3.3",
"lucide-react": "^0.471.1",
"mobx": "^6.13.5"
}

View File

@@ -1,5 +1,6 @@
import { R } from "@/reffuse"
import { createFileRoute } from "@tanstack/react-router"
import { GetRandomValues, makeUuid4 } from "@typed/id"
import { Console, Effect } from "effect"
@@ -12,13 +13,11 @@ function RouteComponent() {
// Effect.map(() => "test")
// ))
R.useSuspenseScoped(Effect.addFinalizer(() => Console.log("cleanup")).pipe(
Effect.andThen(Effect.promise(() => new Promise<string>(resolve => {
setTimeout(() => { resolve("test") }, 0)
}))),
Effect.tap(Console.log),
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>
}

View File

@@ -99,13 +99,39 @@ export class Reffuse<R> {
): A {
const runSync = this.useRunSync()
const initialValue = React.useMemo(() => runSync(Effect.scoped(effect)), [])
// 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 scope = runSync(Scope.make(options?.finalizerExecutionStrategy))
setValue(runSync(Effect.provideService(effect, Scope.Scope, scope)))
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],