From 22e5bbfcc233abd6f11322c984224947c1a4ed36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Wed, 22 Jan 2025 03:18:20 +0100 Subject: [PATCH] useMemoScoped --- bun.lockb | Bin 152816 -> 152848 bytes packages/example/package.json | 1 + packages/example/src/routes/tests.tsx | 11 ++++----- packages/reffuse/src/Reffuse.ts | 32 +++++++++++++++++++++++--- 4 files changed, 35 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index 2cfc90528c15b61d8655bc431e6e72d3aa365735..5e9fd1ea742d9020c52f41ed6fd820bd82ee9316 100755 GIT binary patch delta 6772 zcmeI1d5le09LMi{L(Ma`F&KuL(x^c(47HohD5XP zo!)i3yz8>v(tU&K&+;{L9OoI(BD>JioMf?V?190cMUH|TQ2b@63M0&;vGTc~{CKDf zg;p;%mpG2kd4ftQXoJPzh1FJHi?zt=Xua8wqhTv($K?ddTSbyhmS zSf4eB!#abemS};iQ*V#eVJEBi#=4Y)u?}n$)*|b`rdqrh>wp*F8h9nHgSX%~ya#K4 zzKiqA36H5XW3R=yB>{G$pJ)2TS~EGsKpVs%+} z^>wSu$}h0G?D6j@jE_}xkwxVw|AoT%{sf(6)RbLrSy{yu7GG&`S?yMtS6f`J;_p=y zuV1^f-l7k(n)t0_I(8N&pE1`4?*!ZuR;rarv^f`@`DF`l0+~`RiDz zn_7h&ysW{1I=E#G?qJpLTKyi@+p!iGTOG#fqTgrz{fQ^8RD$InWcA-TDztwJ)PjM~ zAF@R1?9*w6b;FXdnzYf%tOH4=u67+Q9&Uv?TRhwfb+vd`9|d*L-2$@eJ*{4umG4bn zXOL>`)2+R%cm~$rk6{*q!+OY8TYU}ICG}%HBU`YR%B=0SSzcDZ+hui79_7E4 zKP57xj0`CwLrQ)Qp`oRIER_u zZeoW46!nFa2HL|F_Iee5107i^PJ?vFfXu*w3GPha7>b&_?TLGUrmXIApqi|^4R=5j zc{iXH?OHOgI1 z0-7vH0Zk%|0)1$nW~cfPEyr)cc31}**l3Wm6g00^1cfl$!`+%jVLBXwj1gv0nCjG;UkX2TSi1$i(Brovd51`}W|6v1>TfEmy+ zm(@flgqbi7@?jE;hZF2X6KbWJE;KjmbfkVCzrTuq8hcO z4Kq!p)5}{Vm8G=KYVN+8+%BbEinIUV>+a?e-oq84PSOISE8KLw3|Ru@6>h0F|9mKQ zZGWrt?kDrS$vNzlB`t7uh$khc%&M1iJb|$lZbBd{$FtbmaA>GeQs8usr;9h=s-=P0 Ip`IWA1}hLKe*gdg delta 6737 zcmeI0d2Ccw6o=m%5IR7iw3Jd2C{Zb(6j>CZw$QQ&3KS{9$X;m6Qnmsuv{s zF;EIs-S4>WtO}=8N|MSx3z~`VArgKD4fG;3h8j?ks-v5xwWtPEhsN^PLpAUSb!Mup zmx~4%<+BRWsGgve6+D49z)nKdVK=i=P)%h9s)3C`m82TjH1p3#HQ*Ao5xNSELbsqX z==-Sd=c^*qgv=o{9zBn0YJWl1@G7bls(P|(_@y_jJXF87Hwtr9qPMR!9()% z{zFHkCrYr9-c2<_J+1tKs8;?1>F7W9E>X zS-Tll|32z*T>lpZiA_~A->OSpe`H}?7o5&KS!e;O3iEX_m4AWRLG`B;w&|jm__}$e zs#RjPR9T<-OJ(>g3gdigwamQB>VB!qAI*?j!>2V9sHW1 zcda~B<$BNjQvGavis}b$FRFnYMEP?L>0+vW4`WM@_r9Yxt;1^7PMU_HTz5a$@deQ1#wt7;H@lPwi zhN`GWmpfGLZ+M((!3sC6!7Xc0i|SW0k~vj}(VE*kR12m#eig-A`Tf-OuPuu6sa0!g zYeWgCUP2$s=Ic^R=waCbBC&GcdH+&a`iC3RPB43jrxL)WOLk2)gXm(%}=Uz z(8uaapC(vzc!V9-0P!Mjr5uHDg6Qh;*Sn zkW~Msmzpir4EUCrLn_B|R1M2et&KHiuSGSb8&Iv0EvVAnRP8FPT&jMznO2%#ZRyWn zKYwg^PZ{1*hWC{EAcA{KeINF^BC24q1sXeCF zMR-pcTog{SZ9Jq}DB(TjzuDw!#{LI;%GxHiuJcJ*S=*mFWZ3)e&qtu|Ku<8xd;`oU zGc|BB+l@&{S2<~*6=|>A&wPW-r=wth^Sxv~?S)4M+UC&3XI3wjVaaCeZGC9Snw-VF>8ZGZfN64?7HAf#J|M zkdy1S>7w&eZ_phsao6AB575X`(caJp(m)f?FR&-qo#j*fHW{-QDA;t1fhu127u*EJ z@~%UB>UDsAkOHajG&~2rp)2%(PVhYRgNKm;^`hENy0;KJWLh)%VgAPID^(Rutjyh>hY#u8YlGhr#2 z#jpsp?2AFKPGOC$@D@~nAKrjCWWyx*5I%t2uoHH{9?)7#2ppZ_PV%)P+Zx(H6f}ee&;laiQBb@r8X7|rXa>z8 z7UG~KJO+&*2IAoYZ@3he!ZK(FeIO0ep)c@d_#Ax-Ga;J^Qk*#s6c|4Z3ME8AQ)mK> zp(QAU&=nL?7y~+Xp5UfBmnzt_9kziYU)tGdm!oK#B5Z|F0E+wNDG)WC%syDb4Znng za1k!S6}SrDLNuMnKr@Jicu*+dByAPhS`7*p&Vi%w6?_ffz+u`eO5dFNr?BV3cW}nX zr5*+K;Vk)cPz`OVsMEb7UzFMU3V&;*+ dpiEDtx5mtfz?Do-5AU!Mw<}KwbQ$3}{|~=N7mxq| diff --git a/packages/example/package.json b/packages/example/package.json index d73afec..440093d 100644 --- a/packages/example/package.json +++ b/packages/example/package.json @@ -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" } diff --git a/packages/example/src/routes/tests.tsx b/packages/example/src/routes/tests.tsx index 5528315..a019c4b 100644 --- a/packages/example/src/routes/tests.tsx +++ b/packages/example/src/routes/tests.tsx @@ -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(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
Hello "/tests"!
} diff --git a/packages/reffuse/src/Reffuse.ts b/packages/reffuse/src/Reffuse.ts index 389601f..aea43a7 100644 --- a/packages/reffuse/src/Reffuse.ts +++ b/packages/reffuse/src/Reffuse.ts @@ -99,13 +99,39 @@ export class Reffuse { ): 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],