Designing Async-First Signals
By Ryan Carniato
Summary
Topics Covered
- Async Signals Rethink Solid 2.0 Foundation
- React API Design Masterful Despite Flaws
- Signals Enforce UI Consistency Guarantees
- Suspense Inverts Control for Layout Boundaries
- Deferred Disposal Enables Zero-Cost Transitions
Full Transcript
All right. Hello and welcome to my stream today. Sorry, I'm running a
stream today. Sorry, I'm running a little bit late. Let's get that mic up.
All right. Check. Check. All right.
How's everyone doing today?
All right. All right.
I think uh the stream today might be a little bit quite a few controversial topics actually.
Um I'm curious where things go. Hoping
we getting close to solid 2.0 and and I mean you guys aren't going to love what I say in that regard because I was very very close to where we needed to be for solid 2.0 at least on the client side. I
was about ready to move it into like alpha stage and then I realized some things that needed caused me to rethink things about 3 weeks ago and that's kind
of where we are today. Um and yeah, there's some there's some controversial stuff. Um really honestly uh I don't
stuff. Um really honestly uh I don't think everyone is going to like what I have to say today. Um which is okay. Um,
it's kind of like when we talked about splitting effects, but I I want I think it's important that we talk about it because from my perspective,
what I'm talking about isn't really like we can always say that like this trade-off is not worth that trade-off.
But I think once we make certain assumptions or look down a certain path, there is a certain underlying physics to how things work. You know, like I often
say that we discover stuff, we don't invent it. That's what it feels like. Um
invent it. That's what it feels like. Um
here I think there's some truth underlying everything. So um maybe our
underlying everything. So um maybe our compasses are misaligned but um maybe we misinterpret what we see but
generally speaking we take scientific method and approach the stuff logically we will find that we often all come to similar conclusions um ultimately. So I
I I just want to put that out there. But
yes, I've been I've been working like crazy on stuff. Um, and I'm really excited to update everyone on what I've been talking uh, but what I've been talking about on the line a bit, what
I've been looking at. Um,
yeah. Hi everyone. How you doing?
I'm doing okay. Um,
I've uh definitely it it's been it's been a little while, you know, like at this point I've been like so far down R&D place. It's kind of funny when I go to
place. It's kind of funny when I go to like a conference like I was at JS Nation US uh uh last was it beginning of the week? Yeah. And I was like giving a
the week? Yeah. And I was like giving a talk on stuff that I've already like moved beyond a little bit. Um,
so it it is it's surreal when you're kind of like really like I feel almost detached when I like get really far down these R&D things.
But on the positive now, I feel like I know a bit more now. I've I feel like I've implemented like how many different reactive signal
systems and how many different um uh like concurrency models now? Like I've
done it at least three or four times. um
over the last 5 years. So like I feel like I'm I'm learning something each time.
Uh hi everyone all coming in. Let's go
Buzzy. Yeah.
[snorts] Um yeah, arguably that bad maybe.
Hey hello.
So, no, no, nothing like that. No, I I think we'll get into it. I made some notes for today so that that we can cover everything because there's a lot of
stuff. But I think something you have to
stuff. But I think something you have to understand about like the the the truth and the reality is I will sometimes give flack to react in terms of assumptions they make because their implementations
differ from the way that I view implementation. And I'm like, okay,
implementation. And I'm like, okay, well, we can actually tip the model on the head and we can get benefits you can't get other ways. This can change the trade-off slightly. But the one thing that I have a hard time faulting
React, and this is something very odd for people when they look at like alternative frameworks, is is their API design. I think React's API design is
design. I think React's API design is topnotch um in terms of like going, okay, this is the problem. How do I design something to fit the shape of the
problem? Right? I think every I think a
problem? Right? I think every I think a lot of times it's tempting to cut corners and I think a lot of times it's like we try and like get around things and it's funny because API is what
people interface with. So it's also the things that people um like dislike when they when they use React like why do I have to do this? Why do you make me do this? Why do you make me type these
this? Why do you make me type these extra characters? All like all this kind
extra characters? All like all this kind of stuff. And I mean sometimes a
of stuff. And I mean sometimes a framework author it's hard not to be like you know treat people who complain like that like little children you know
who are complaining that you're giving them their vegetables but like it's there there's usually a very good
reason for this and maybe that reason isn't completely understood but that that's not where we're going to start today. I just wanted we're just I'm just
today. I just wanted we're just I'm just calling shots and letting you guys know that um today's stream might be a little controversial placing signals with this update. I
mean, not really, but I think I think this upupdate is something worth talking about today as well. I Yeah, we're going to pull into stuff from Remix 3 from that perspective when we talk about
timing and examples that I'm going to talk about today. Um I think today's the day that we finally uh look at Joseph Anna's talk. um from React Comp because
Anna's talk. um from React Comp because it's actually it all relates and um [clears throat] in into this.
Yeah. I I I mean to be fair, the tpples that came back from from hooks was very like it was like what I was looking for. I think
the one challenging thing is like from my perspective it's so funny because like mo big frameworks like Vue and like um
yeah like Vue actually only one and Angular doesn't have really the equivalent like hooks was like from my perspective hooks were not the front of the they were like at the end of the
chain like other frameworks has had things that look like hooks way before reacted and not just like a couple like many. It's actually like they had those
many. It's actually like they had those and then kind of got rid of them because of React. So um but from my perspective
of React. So um but from my perspective the the tpple in React to get the read write segregation is like one of those like nuanced things that are just
masterful stroke even if like the hooks themselves are feel derivative even if they didn't really give them credit but like like generally the the the
tpple is just genius. Um, but it's a that is the exact kind of API nuance that that I'm that I talk about. So
that's fair.
Um, okay. Yeah. I mean, and and just just we're going to get to this in more detail as we go, but the idea here is um we want it's this is almost as core as
why signals are different from observables, right? We want to and I'm
observables, right? We want to and I'm going to use this opportunity right now to to to pull up pull up my X and some
other stuff in the background here. Um
it's it's that like RX is an event system essentially like there there are different versions of reactivity you know and you there's very thing but RX is generally an event
system. Um it's actually more similar to
system. Um it's actually more similar to remix 3 of all things funnily enough.
It's it's like you just feed these channels of events through and you make sure that like you get some eventual consistency that way. Um but that's the the the guarantee. They can be synchronous. They can be async or not.
synchronous. They can be async or not.
The the thing that makes signals special and reactive frameworks in general like React or that special like the reactivity is really an enforcement of a certain type of guarantees around
consistency. The idea is that
consistency. The idea is that you only you can only trust your UI if you believe it to be consistent.
Otherwise, you're going to have to like people won't trust it and they'll refresh the page. So you have you want to give the end user a sense of um trust
by being consistent by making sure that whatever they do looks consistent seems you know doesn't give them like oh is this really updated you know kind of
mentality and then secondly um developers also kind of fit into this that if they don't see that they might not trust their tools or their
frameworks. So like signals are about
frameworks. So like signals are about consistency. So when I talk about async
consistency. So when I talk about async often it's it's it's not like it's not about transformation but synchronization. And I think this even
synchronization. And I think this even with async inside signals themselves keeps this property alive where um my my idea here and this will be much later we
get to this is that signals um are this mechanism to provide synchronization guarantees. Um RX doesn't worry about RX
guarantees. Um RX doesn't worry about RX cares if something fails and you re retry it three times and do this and fork this. It's it's it's procedural.
fork this. It's it's it's procedural.
It's a process that you go through where signals are like it just is like it's like this is the rules of the universe.
At any time I look at it the you know I must be seeing something that I can trust and believe.
I don't know it's a good explanation.
It's the weirdest part about being where I am like deep down the rabbit hole.
No, as usual, this is stuff that I am literally inventing or discovering as I said much more thing uh apt description on the spot. So when some people when
they're they're looking at my content and stuff, they'll be like they'll be like, "Oh, where can I read up on this stuff?" And I do have some HackMDs that
stuff?" And I do have some HackMDs that I've been penciling out. But in terms of the scale of like where we are in terms of the process here, when I stream stuff, it is literally a week or two
since I've had the revelation myself.
that's within that first kind of gap time. When someone writes an article on
time. When someone writes an article on dev 2 or something consumable, you know, on Medium or whatever that minimum they probably been working on something for 2 or 3 months. Um, and
like, you know, it takes I take that content out and then it'll be like 6 months till the conference talk and then maybe like a year to the short to the point video. Like basically this is as
point video. Like basically this is as cutting edge, bleeding edge as it gets on this topic. um if you join the stream and I apologize. I my communication is not as refined as it could be because I
I'm literally researching this stuff right now. This is this is stuff that is
right now. This is this is stuff that is about as you know breaking news so to speak as it can be. Um I've been covering stuff incrementally through my streams for the last year, right? Last
February I did signals 2.0 future async and a lot of the fundamentals and foundations are there that I'm going to talk about today. But what I I I've had
some major revelations the last week, couple weeks, and I I want to kind of talk about what I think that means and what it inspired me to do.
Isn't that a description of immediate mode? I I don't
mode? I I don't I don't It doesn't have to be immediate mode, right? Like it's it's I mean
mode, right? Like it's it's I mean immediate mode for those who don't know immediate mode is like when you like rerender the whole page like when you like basically like game engines are
often that it's often easy to to like produce synchronization when you rebuild everything because then you know like there's nothing weird artifacts are going to creep in. You just kind of go
wipe it out. Fortunately not always very performant. Um, but the the the
performant. Um, but the the the mechanism that actually makes things stay in sync is I view it as a reactivity system. Whether it's the
reactivity system. Whether it's the harness around the immediate mode renderer or a very sophisticated synchronization system around a retained mode renderer like um retain mode is the
alternative. That's like the DOM. It's
alternative. That's like the DOM. It's
things that persist and you just like mutate or change parts of it. Um but the the approach here doesn't actually ma matter that much and it's actually very interesting because um you could say
that React has been approaching this from a immediate mode perspective while I've been approaching it from a retain mode perspective but generally to similar goals or results right and and
there's always the thing that's always funny is because of performance immediate mode rarely stays immediate mode like there's always like some kind of exception some kind of persisted thing like hooks themselves for example
in the component model in React like you you don't just blow everything away even in immediate mode um like even theoretical because performance will
always take you back um right I'm trying to think of like other examples like uh in game engines like um Vulcan uh my understanding I was I was talking to
this guy a while back at a conference and he was explaining to me how they were actually looking at techniques of re retaining parts of the render graph And it wasn't just throwing everything
away every time. Um, which is really interesting to me because I see parallels there between, you know, UI research. I mean, this is probably more
research. I mean, this is probably more relevant to people like work on the React side where they're looking at how to bring more performance into basically immediate mode rendering, but it's still
very interesting stuff. Anyways,
um, did I though did I ditch transitions?
That's That's the thing. We We'll talk about that. I I I I love getting you
about that. I I I I love getting you guys all warmed up on this.
Okay. So, let me pull up my notes here for a second so I can remember what I wanted to talk about. Okay.
Yeah, we we should actually start with um Yeah, I Yeah. Uh sorry, I love this.
Yeah, double buffering video games is basically beat on. Yeah, I mean Yeah. God, it takes me back. I sorry for
Yeah. God, it takes me back. I sorry for those of you who don't know, I got into programming because I want to make video games. Um, so it's kind of an
games. Um, so it's kind of an interesting thing because you think like and and you find this a lot a lot of the like like people who are really invested in in these realms especially in
graphics quite often um are immediate very functional [clears throat] programming immediate mode kind of people and I think that would have been my um my positioning you know arguably
but then I realized that the DOM isn't I I realized two things well first of all the DOM isn't immediate mode which meant that like trying to enforce my abstraction on top of it was never
going to be the like most optimal at least from my perspective. And the
second thing is I found something that I liked even better than video games. And
that was um it's so funny. It felt like a soduku or like a puzzle um digital circuit logic. And I actually went into
circuit logic. And I actually went into um computer engineering and and programming like like almost like old style arcade games with like circuit
boards rather than like a conventional programming language is the funnest thing that I I I don't know how else to put it. I just found it so incredibly
put it. I just found it so incredibly fun. Um the like I'm I'm a weird person
fun. Um the like I'm I'm a weird person who thinks like I I don't think verilog or VHDL is beautiful but I think that
the systems that work like that there's some kind of it feels very powerful or some kind of truth when when you describe things as like a state of being
rather than a grocery list. Um anyway,
okay. So
classic. He wanted to make videos in the front end. Yeah. Yeah. Well, I mean it
front end. Yeah. Yeah. Well, I mean it was video games or music. You see the guitars in the back, but I I you know I was those are things that probably
appeal to a certain type of young person into nerdy kind of stuff.
You know, go one way or the other. But
um yeah, then I end up in front end, which I mean wasn't unrelated. I I
started building websites to promote my band. So yeah. Okay. Sorry. Um,
band. So yeah. Okay. Sorry. Um,
why is my screen flickering so much? I'm
going to have to do something about my cable. It's not fine. At least it's not
cable. It's not fine. At least it's not impacting you guys. Okay. So, uh, where do I want to start? I I'm going to start by first just, um, sharing my entire
screen here for a moment, even though that's probably not going to be too relevant right now. Um,
but I feel like for me this journey starts by talking about async in UI frameworks
just in general, right? And yeah, sorry, I just wanted to get that in there.
Let's talk about that for a second.
JavaScript itself is a language that's very unique in that um [snorts] is single threaded right like essentially single threaded I know
there's there's exceptions you can make workers and all this but and it was basically designed to work in the browser um
and what's interesting to me is that the model like JavaScript started as something which It was a weird amalgamation of a bunch of stuff, you
know, like it had like um you know like functional programming pieces with like a sort of object-oriented inheritance like prototype typical inheritance and
something it's a weird language and the addition to of asynchronous features to it um you know didn't follow what a lot of other languages are doing at the time
as well. like instead of like you know
as well. like instead of like you know going okay let's make a new thread or something it was like no we'll just have a really powerful scheduler right and
which makes sense like with the browser you only have one user you're catering to so like while you want to be concurrent while you want to be able to have multiple things on the fly you don't actually necessarily need to be
parallel um that I mean that that's at least a logic to it and you could be like okay well that seems reasonable in the browser but then Rindall went and created node and
interestingly enough also took this model you know because it was using JavaScript and as it turns out I mean JavaScript is not actually the slowest uh language on the server it might not
be one of the faster ones but um the way that it could handle concurrency in terms of multiple requests because let's face it on a server a lot of times you are waiting on IO it's not like straight
processing it's like you know you're like okay request comes in this comes in you know then go fetch that data from a database. Well, while it's fetching from
database. Well, while it's fetching from the database, you don't need to be like saying like doing anything. You're just
like twiddling your thumb. So, like it can go, okay, you go back and do something else. So, JavaScript actually
something else. So, JavaScript actually built a fairly, you know, performant way of switching contexts really rapidly between the different things and a scheduler that supported that. Um,
obviously Ryan had to do some work himself early days to make sure that it could work on a server. There was a lot of things that weren't in the JavaScript spec. you know, you need stuff like set
spec. you know, you need stuff like set immediate and ways of like more control over that scheduling. But we've seen that in the browser over time, even after that, we we've started getting a lot of those kind of tools make their
way back out, you know, the concept of like microtask queuing and um you know, which came with promises and um you know, and whatnot like this basic work
towards a very comprehensive approach to scheduling. Um it's not like
scheduling. Um it's not like multi-threaded. It's not go routines.
multi-threaded. It's not go routines.
It's not like but it's something that's very built in and almost automatic. You don't have to
almost automatic. You don't have to think about it much. I mean you have to understand the timing because you know promises resolve at different times async wait you know stuff that got built on top over time. But generally speaking
you could just set a bunch of timers and your code will just kind of schedule in a reasonable way and that was it. you know, if you've ever used, you know, multi-threaded code and,
you know, thread locking and like, you know, I think everyone's done this when, you know, you go if you at least if you gone to college or whatever, you know, um what semaphors and like the whole like um
I it's been so long I can't even remember the terminology, but you you know what I'm what I mean where you like go through those like classic problems like uh like dinner table or whatever. I
I'm I'm butchering the names of these things now, but you there's a whole category and you have to remember part of this also for
me is my timing coming into this stuff um I went to college university whatever around 20 2001 2002 and that was right
around the time that they were first adding like uh multi-core processors to computers. So like it was a very hot
computers. So like it was a very hot topic this idea of like how can we properly parallelize workflows and interesting enough obviously stuff in graphics was fairly easy to parallelize and that's why they ramped up to even
more cores but there's a lot of processes that we weren't readily really easily able to see how to parallel parallelize right away and without going like way too crazy down this kind of
tangent the thing to know is that JavaScript actually does a fairly decent job of just handling the base cases in a way that is fairly simplistic.
but just kind of just works. Um it might not give you all the control mechanisms you find in other languages but it it is built with asynchrony in mind probably just from the basis of being you know
client server model in the first place like a lot of your resources are not going to be in the browser. Okay.
How you doing chat with me so far?
The soft basket case is marked. Uh
yeah. No, I I've seen some people make games of Solid, which is cool.
Yeah. There's a lot of talk about how it's not like a single scale here.
Yeah. Yeah. Talking about like how Yeah.
I mean, it's kind of like you cache the von nodes in a lot of uh or or find ways to make block nodes like in like in Berno's performance techniques. It's
it's not so black or white. It's at this point a lot of it comes down to mental model and a lot of cases people will argue that um and I think it's fair that the mini mode mental model is a lot
simpler. Um
simpler. Um but on the other hand, I feel it doesn't give you necessarily as much control and I like control uh if you noticed anything. Um but it is an interesting
anything. Um but it is an interesting thing. I I think I think classically if
thing. I I think I think classically if you if you like look at this like stepping all the way back you should probably think that like a lot of um innovation should happen on retain mode
first and then with better abstraction make its way to immediate mode like that should the process things which makes in a sense certain industries um like game
industries or even like graphics like maybe it's just cuz the complexity of UI is so hard that it was easier to actually um go immediate first and and then kind of work backwards is that like
that doesn't make a lot of sense in terms of like the engineering or the mechanics that need to happen behind the scenes. As I said, if you're if you're
scenes. As I said, if you're if you're finding ways to optimize and actually do more stuff like uh like sort of do less work, you you probably start with retain mode and then figure out better
abstractions on top of that. But we've
kind of been the other way around which as I said it feels sometimes crazy because like when you're working on the web and the DOM, it's retain mode. It's
like this thing that stays around. So
having like React being the most popular UI framework is kind of crazy. Like I I I people I don't think people appreciate how crazy that is. And I'm not saying this in a negative way. Like it props to
the to React. You know, it's it it's it's kind of bonkers in my mind that that kind of approach could be the like the most prominent one. I I think it's good to have alternatives, but the fact
that it's like so much more prominent the other ones seems like somebody dropped the ball. Um,
collectively maybe we dropped the ball like uh I I I could picture a world where React wasn't the most popular UI framework and I was a huge fan of it as
I said um you know like for doing something really interesting. Instead,
it's the 4ERunner, which is like, as I said, it just doesn't make sense from my my engineering brain standpoint.
How we doing, chat? You guys are talking faster than I can keep up with what we could hear.
You haven't missed anything.
Okay. So I I'm just I'm just establishing right now like JavaScript is a weird language. Okay. So
um I don't know how many frameworks actually took thought took much consideration into async early days.
That's all I'm trying to say. It's just
because like the JavaScript model kind of like handle it for you. It's like
okay well I will set a timeout or whatnot you know like. And I have to give a lot of credit here. Um, React
seemed to be one of the earliest frameworks that actually were like, hey, we're going to actually make mechanisms for this, right? Um, suspense comes to mind. Transitions come to mind. Um, and
mind. Transitions come to mind. Um, and
I was very very influenced by those.
They both got announced like 2018ish, I think, if we go back and look at it. Um,
maybe 2017 for suspense. There was a talk in React Comp Iceland and then like um, you know, yeah, I think it's the
Iceland talk. Anyways, the what what I'm
Iceland talk. Anyways, the what what I'm trying to get at is you get the sense that React for a long time has actually been thinking about
these problems. Um they as I said something we might not appreciate at first like early on I might have you know been like worried about like it's very easy when building frontend
libraries to kind of like focus on like how do I make sure my UI is fast and all my user interactions are buttery smooth so to speak and that is a synchronous
synchronization problem. Um, and I'm
synchronization problem. Um, and I'm like I'm going to call right now like there if you're looking at performance in that and like fine grain rendering signal, it's hard to beat that kind of
situation if that's your whole world.
But as I mentioned be before React started, you know, uh, fax.js back in the day. Did Did we ever pull that up? I
the day. Did Did we ever pull that up? I
can't remember. I I I I like fax.js.
Let's see if I can find it. Jordan Walk.
Yeah. Okay. Let's Let's just pull this up for fun. Um,
not that one. This one. I mean, this is 14 years ago. This is an old experimental project. Rack is much
experimental project. Rack is much better in every way and you should use that instead. But basically, he was just
that instead. But basically, he was just trying to like copy stuff from the server into the client. And this is kind of where the starting of the whole VDOM experiment started from because like I'm gathering we're talking about diffing
HTML strings in this thing. Um, they
didn't quite have JSX. there had you know like all the the some of the pieces are here you start understanding the
model this is probably not you know I'm just going to post it here just for fun but it it is interesting um nonetheless
so yeah I it's it's fine it it's a historical note, but what I'm getting at is sometime around 2015 16, React was
like, we're going to plan for the future. And async was part of that
future. And async was part of that future. And I think it's important to
future. And I think it's important to understand that because a lot of certain like decisions were made from that point on that people might not have been able to justify until the final picture kind
of started showing up, which literally took till about this year. Like I feel like it's been like 10 years, but like there was things they did that was controversial and we'll I think we're going to see as we go through the
meaning of it like why they made those decisions, but um we should fast forward a little bit,
right? Um are are many of you familiar
right? Um are are many of you familiar with with um with with these features at all? I I feel like if I if I just pull
all? I I feel like if I if I just pull up like I'm going have to pull up my stack blitz right now. Of course I'm not signed in on this computer. Watch it. I
won't be signed into GitHub GitHub either.
[sighs] It's been a while since I've streamed.
Sorry. Give me two seconds.
actually. Okay, we're in. Okay, we're
in. Um,
I'm just going to pick one of my random examples to just really basically show the kind of features I'm talking about.
Um, That's is it switching tabs?
Let's do where is it? Sorry. I've got so many solid to experiments tabs. This is
probably the one I'm looking for. Now,
this is a solid example. Um, solid 2.0 to be precise, but this was very inspired by React for me. um
source and very like you can kind of see some of these things in action. The idea is that there's a couple
I'm going to actually remove the transition here just for for sake here.
The first feature that was added is suspense. And at this point suspense is
suspense. And at this point suspense is available in a number of frameworks. Um
the idea is you have some app state or whatever some of it might be synchronous but the sometimes you have like this child
component which kind of fetches some data right um I'm going to use solid syntax here because I'm using solid but the idea here is you create I'm just making a delay but it's random but
essentially I'm just going to fetch some data when I go to this go to this page and essentially if if you don't have the data, how do you render the component? Right? There
there's this kind of thing, you know, problem. You can't render something
problem. You can't render something beforehand, but maybe you still want to render part of the part of the page. So,
when I refresh this page, what you're going to see very briefly is this loading indicator where you see the tab bar, but then the loading comes in. I
think everyone can kind of see this. And
um this is pretty standard um in a lot of frameworks. You have suspense in view.
frameworks. You have suspense in view.
You had I think an await component in spelt uh back in the day. Um we also have our suspense here. And I'm showing this loader. So basically there's some
this loader. So basically there's some semantical difference with solid because um suspense is a really cool technology because it inverts control um
essentially it says that unlike say a weight component um which is basically like don't show this until this promise resolves like you could you could picture you could do that with almost
any state right if you have a conditional like uh like um uh turnerary like show this data like if
data show this u UI. You could always basically just have some state and then go like fetch data and then set state and then continue like that kind of stop
pause um mechanism you know maybe fetch use a factor create a factor whatever was always possible to a certain degree
but what's interesting about suspense is it lets the children render basically offscreen which means it discovers it like the child isn't until we're down
here right So, we fetch this data below the suspense boundary. We don't know um that there's even going to be anything to fetch. If there isn't, we could
to fetch. If there isn't, we could probably just show the page right away.
But, but this is was a masterful stroke in the fact that it it was like conversion control. It's like look, I'm
conversion control. It's like look, I'm designing my layout in my UI. I want to know where, you know, this is where the loading importance. This is where the
loading importance. This is where the natural page boundary is. This is these are things that that make sense in terms of the design aspect of my page. And if
people add async later, this is where we fall back to. This way you can reduce like a bunch of intermediate states. We
were kind of in this problem early where it was like if everything was managing its own loading state, you'd kind of have like a dozen different spinners.
And this was like no, okay, look, I will handle the stuff that's not available.
You know, you can always nest suspense and make more of these states. But the
idea behind suspense is that you can kind of set a natural boundary. Like ch
child could be a lazy component that you don't have the code for yet too. It
could be something that fetch data. All
these aspects don't really matter to suspense. It just
suspense. It just puts a loading indicator till it's available.
Hey Mark, you you didn't miss very much.
I was just talking about about why JavaScript was a weird language. Yeah,
and you're right. I can put some of these experiments in as I as I go. This
one I I had to butcher a little bit because I had to remove this is this this exact example is actually if you go to saltjs.com and go to our tutorial
and again this is like way down I want to point out that async is like way down on the list we we get to an example um like the first example with suspense is
really easy it's just like hey get me a promise right and then it's like greeting and then What's greeting?
Oh, it's a laz it's a lazy component, right? Um, so I'm like faking lazy here
right? Um, so I'm like faking lazy here through the import and making a delay.
But and that's how actually React released in the first place too. They
they actually released it by a way of doing uh lazy code loading basically.
That way they can show something if the code isn't there. This code splitting makes it a lot, you know, more performant. You don't have to load all
performant. You don't have to load all the JavaScript for the whole application up front. You can do it a page at a
up front. You can do it a page at a time, you know. Um,
but the example that I want to get into is transition transitions. Um, which is this example exact example. I've been
making this forever essentially where you click on a tab, you update the tab, and then you have suspense wrapping it.
Right? So, I'm going to I'm going to this like we've had transitions. It's
funny. I I looked at React's work and we actually released a few of the features before them because I was like so into it once I saw that first Dan Abramoff uh demo. Not the I didn't care about the
demo. Not the I didn't care about the stupid radar thing. It wasn't about performance. People a lot of people
performance. People a lot of people dismiss this stuff because of performance like oh this is React's way of solving its own problems. No, no. I
was interested in this because it was immediately obvious to me how difficult it is to solve uh these concurrency problems in a consistent way which we're
going to get into detail on what I mean by that but yeah um this tutorial on the solid site is is an example of this example I it's it's the same it's the
same thing the reason I like the tutorial I share that one is because it actually has a before and after but the the key part about suspense is that um it just doesn't care. So when I go here,
guess what? Loading. Go back. Loading.
guess what? Loading. Go back. Loading.
Every time I switch, it just drops it out. And for situations like this, this
out. And for situations like this, this is pretty normal. You're kind of used to seeing this. And as I said, nested
seeing this. And as I said, nested suspense. What if I
suspense. What if I um put another suspense in here because I have this counter. And this counter is here for a very important technical reason that we'll get to a bit later.
But you notice this count is global. It
comes in through props count. So all
these components have the same counter on them essentially that goes through that's just getting driven from the app, right? And if anyone knows solid, it's
right? And if anyone knows solid, it's not a big deal. The the components don't rerun. So we're we just have a signal
rerun. So we're we just have a signal that's updating, you know, the RH3. But
what's interesting is what if we wanted to be able to show some of the content sooner than some of the other content.
So if you notice the my child component here typically is just you know fetch some data display it but then with uh
the child with suspense example I've wrapped an additional suspense wrapper around where we actually use that um fetched data so to speak. So it can
actually render more of the page before it hits it because suspense always goes back to the nearest fallback. So, what
I'm going to show here is like if I go here, loading, loading, but on this one, what you're going to see is the count the Yeah, let me go here. The count is still
there because I put the suspense boundary below. It doesn't trigger the
boundary below. It doesn't trigger the outer suspense. See, this one's just
outer suspense. See, this one's just loading. This one's loading um
loading. This one's loading um loading T. So, so to speak, right? So,
loading T. So, so to speak, right? So,
like or Trey, what am I saying?
Whatever. Am I French or Spanish today?
I don't know. Doesn't matter. Um, but
you see it's it's actually a different loading indicator. See the specific one.
loading indicator. See the specific one.
So this inversion is really really really powerful. It means that the
really powerful. It means that the application developer can decide where to put the loading indicators. It's not
about like component like I actually don't think suspense belongs in reusable components. Maybe people disagree with
components. Maybe people disagree with me, but like conceptually it's a way for the application developer to go like this is how I want to lay out my page and I want to make sure that um you know I can show stuff in a reasonable way,
put skeletons and it's do what I want and I don't care where the async is as long as it shows it in a consistent way. It it basically removes all the complexity around uh
coordinating these things. And it has a second really important use. It gives us a boundary for streaming. um you can basically go like look these are the blocks that we can hydrate synchronously
as they come in and you know we can basically stream and this was something that was very much on React's mind right from the beginning of introducing this API um Marco had something very similar
called async fragments um back in 2014 async fragments I I sometimes joke that Marco invented suspense back in 2014 um
but if you look at actually what what he's talking about in this classic article. He's talking about streaming,
article. He's talking about streaming, but it's a very similar idea like essentially he's showing like the sections of the page and then he gets here async fragment provider data search. Now, this doesn't have the
search. Now, this doesn't have the immerse of results. This is not a weight. This is literally, you know, um
weight. This is literally, you know, um the await tag, so to speak. So it's not quite the the same, but the idea that you can section up parts of your app
based mostly on like layout considerations. Like there's data
considerations. Like there's data loading considerations. You can't
loading considerations. You can't separate those things, but you like it isn't about what makes suspense so powerful here is it is it isn't necessarily about
just like awaiting this data, you know, so to speak, even though that's like effectively what it does. It's about a layout thing. It's there's a reason it's
layout thing. It's there's a reason it's a component. It's it's it's something
a component. It's it's it's something like a designer kind of thing like um I'm actually considering renaming this in the future in solid um just because I
think suspense is kind of like a doesn't convey that enough just just in a side note. I know it's become the kind of the
note. I know it's become the kind of the term but um as you see today we're going to change the behavior suspense enough that I don't think it's might not be suspense anymore. So, um, I think it's
suspense anymore. So, um, I think it's worth putting out there, but I I'm hoping you're kind of understanding how how we kind of get into async solutions here.
So um we've seen these examples. Now, the the challenge here, and I'm going to actually pull up one of my my talks because I talk about this stuff a lot.
And, uh, let me sorry, just going to grab my Beyond Signals talk that I just gave because there's a great slide in here. And
I'm kind of getting ahead of myself a little bit, but we're going to come back to this slide. When you're dealing with async, there's really I we've talked about this on stream. There's really
like four different scenarios you have to consider in terms of how you can handle things or you don't handle them and tear, which is kind of like the default. But what I
suspense is show the placeholder generally speaking. Um, and that's kind
generally speaking. Um, and that's kind of like the starting point. The problem
with the placeholder is not all experiences want like picture if there's some async on the page like you can cause this to refresh or parts of this to refresh you
know after the fact. It's kind of jarring that like you're doing stuff and then I mean I can make an example that does
that very easily. Um, and the reason is suspense is show placeholder, but it's basically bail out. It's like, look, the thing to
bail out. It's like, look, the thing to show is not ready, so we're just going to fall back up to wherever, you know, we know we're safe to show.
You know, it's consistent by the by the method of bailing out. But realistically
we have to consider that aspects of all four of these are actually important to us. Okay. So um there's other mechanisms
us. Okay. So um there's other mechanisms here. Um just to summarize even though I
here. Um just to summarize even though I want to get into the the the detail of it a bit more as we continue but show placeholder is basically suspense. Hold
in the past is a transition. It's
something um that we'll show. Show the
future is optimistic UI or optimistic updates. and don't pair um is what
updates. and don't pair um is what happens when you don't use these features. But I think one of the the
features. But I think one of the the biggest challenges is if you start opting into these features like suspense, how do you opt out of them?
Like if suspense is this whole boundary and you're like I don't I actually don't want to go to the fallback. I want to tear um classically for React this was
use deferred value and um solid we had something called latest um which lets us get out and lets us tear. But what I'm
getting at is all four of these are things that you need to consider when looking at an async um solution. Okay.
So, why are we talking about this today?
Cuz that's fine. Um you know, I I've shown
that's fine. Um you know, I I've shown this stuff before in stream. Um I'm
going to actually return the transition back in here. There we go. Transitions
back. And just to show you really quickly, it loads because it has no data. But now after the fact as we
data. But now after the fact as we switch, you can see that it doesn't actually remove it. It might gray out if it takes a while, but we're actually holding stuff in the past while we do
the next page. This is what a transition is. I'll explain in detail how this
is. I'll explain in detail how this works in a little bit. Um, we've
actually done a stream on this before, but um, what I wanted to kind of just highlight here is here's a feature that allows one of
those other modes. So I was pretty happy I actually implemented a solution for all of the above and that's why I started giving that talk because I literally solid has a solution for all
the above. It looks a lot like the react
the above. It looks a lot like the react solution unsurprisingly. There's some
solution unsurprisingly. There's some detailed differences because of the fine grain graph and stuff but generally speaking it, you know, these are the
same modes. I talked about stuff being
same modes. I talked about stuff being discovered. Um it makes sense that um we
discovered. Um it makes sense that um we would you know like even even if I could like think of slightly different ways to do stuff, we kind of end up at the same
place. You know what I mean? Like the I
place. You know what I mean? Like the I mean rename suspense component to placeholder component call transition hold like you know what
I mean? Like these things almost line up
I mean? Like these things almost line up one for one with concepts. you know, you just name the feature, right? And then
come up with the API that's suitable for the feature, right?
That that's a very Yeah. Yeah. Like a weight. Yeah. Yeah.
Yeah. Yeah. Like a weight. Yeah. Yeah.
Exactly. And spelt used to have a weight as well. So I think suspense is like
as well. So I think suspense is like really really powerful because the inversion of control like that it's read based. Um,
based. Um, yeah, looking at the code, we we talk about this high level because looking at the code of this stuff is freaking tricky.
Okay, so Oh, it's not it's not quite the same. Okay, fair enough. What people hit
same. Okay, fair enough. What people hit the same problems?
You use a way with suspense. Yeah. Yeah.
We're talking about Remix, too, because they basically needed a way to do streaming and React hadn't given them all the tools yet, so they started building stuff like halfway through.
Yeah. I mean, transition is a hard one.
The problem is I was calling them transactions for a while, but they're not actually transactions. Transactions
um suggest a discrete like action within time and the cancelable and roll back.
and maybe somebody when they these become parallel could. But what this actually is is a gradual progression to an eventual future. Um we'll talk about
exactly what transitions are in a little bit. Um Angular's defer to my
bit. Um Angular's defer to my understanding has a lot of similarities like I know the use under like specifically like lazy.
I I feel like the fur is a bit if you took like an await and like lazy um from React uh sort of like took an await which is kind of like suspense and lazy
and just kind of jam it together in the same thing and then you're like this block is something that's lazy and does that I I the interesting thing is we've kind of split these into two
different concepts at least on the React side is that like um there's both the bounds in which you want the UI to care about it like in terms of a design standpoint which suspenses and then
there's like the me mechanism for signaling off a block to be loaded later or to be hydrated later which is what lazy does. Um
lazy does. Um yeah it's just it's Yeah. Yeah. It's
lazy. It's lazy loading components.
Exactly. Yeah. Exactly.
Yeah, the fur could be general on both laz loading data fetching, but they haven't said anything yet. I mean, it's fine.
Mostly it's for incremental hydration, which is fine. I mean, that was something I was always tempted to add, like add like an option to lazy to do incremental hydration. I just
incremental hydration. I just incremental hydration is challenging and I think it's a whole other topic. I
think I haven't figured out how to make it safe cuz the problem is sh state is shared and if state can change outside you can't hydrate it again like that it no longer
matches I I I don't actually know 100% what angular is doing there they probably should look at it maybe they because they can use signals they can go like okay I'm I'm going to wake it up anyways like kind of like quick does
like basically they just void that because you know I think that's a direction they're going at least because like whiz and quick need to do similar stuff.
Yeah. So, it's it's it's it's a little bit it's it's the same problem I have with like islands. Like it's it's very easy with islands if you have them
communicating to each other for them to basically do some client ups. they which
allows them not to hydrate anymore which is the reason that re react server components hydrate eagerly if possible and after the fact only render on the
client like not the server components but the use client like it can do SSR initially but after the fact you can't there all bets are off you have to run them on the client because some shared
state might have been updated and you can't trust the server anymore the server doesn't have the information unless you want to freaking serialize the whole thing and then we're back to
ASP.NET And trust me, ASPnet.NET is not where anyone wants to be. Um, okay. So,
see, it's so easy to get kind of tangented in here, but I just wanted to kind of like highlight it seemed pretty straightforward because from conceptual thing, we had this and then, do you know
what happened?
Spelt happened.
I mean, the same week that I went and did my stream, uh, maybe it's because I did the stream and Dominic Ganaway comes like knocking on the Discord and it's like, I want to I want to I want to show
you something we've been working on. You
know, this is while he was still working on the ST team and he's like, I think we figured out the model for async.
And he's like, I don't we don't want people to worry about like suspense and to worry about um you know transi transitions. Like we obviously we need
transitions. Like we obviously we need something like suspense, but we don't want people to like worry about like the the rip away feeling. I'm like I agree too. We made a model that doesn't rip
too. We made a model that doesn't rip away either. And he's like, "No, no, no,
away either. And he's like, "No, no, no, but like this will never rip away and there's no transitions and you just do the thing." And let me see if I can pull
the thing." And let me see if I can pull up an an example to kind of show what I'm talking about. Um,
let me grab one off X. I think I had some. Uh,
some. Uh, I think Rich Harris's original example is probably the best one here.
Uh, async Holding. Yeah, let's let's pull up
async Holding. Yeah, let's let's pull up this example from I talked to Dom Dominic was yeah he was a little bit frustrated at the time because he thought he had this really
cool model and they weren't like the rest of the team wasn't sold on it. He
said over time that they basically ended up to where he wanted it to be. He he
said it just took a little bit of time.
So I cuz I I asked him this the other day cuz I thought that too when I saw this model come out. I was like, "Okay, maybe this isn't what Dominic was like after, but he's like, it's basically got
to where I I wanted it to be." So um well this example um
I I I don't have a solid version right now but the classic example of this and this is felt syntax you have to take a moment to get used to it with the new
stuff but essentially you have state and then you have derived um async uh computed or memos depending on it like
the derived values And essentially there's a counter and then we await a multiply which is just faked out to be async based on it's like half a second
um up to half a second but essentially it's just calculating this function and and each one just gets inserted in the DOM.
>> [snorts] >> If you had a naive pairing approach, I mean, we can probably just build this
example in the solid playground really quickly. Um,
quickly. Um, I kind of want to do the old solid 2.0.
I I I think most people I'm I I don't have it right now. um to
be fair uh pre-built and I probably should have but when you look at this code where you're just like clicking a
number doing some async stuff and that's it. Um for the most part
it. Um for the most part I actually added this part into this demo. Rich's demo originally didn't have
demo. Rich's demo originally didn't have this. Um we can delete it for a second.
this. Um we can delete it for a second.
Rich's demo was like this. And you'd
click the button and it wouldn't update right away. It
wouldn't update until even though these are random, um it wouldn't update until all of these kind of came in, right?
Whereas like if if you see this demo, I feel like actually, you know what? Maybe
I should just remake it like really quickly. I I I think it's really
quickly. I I I think it's really illustrative.
Um, the best way to do it though, the best way to do it is take, actually, the best way to do it is
take one of my previous um, ex solid um, 2.0 examples um, that I did on the playground, which
means I might have to go back a little bit. Um,
bit. Um, it's funny. I've probably posted
it's funny. I've probably posted something very similar to this before on Blue Sky. I'm just not going to find it
Blue Sky. I'm just not going to find it right now, unfortunately.
Um, yeah, I'd have to go digging through Discord. This is so frustrating. I don't
Discord. This is so frustrating. I don't
feel like doing it right now. It's This
is too much of a tangent. The the thing is it's hard to show in a system that that protects against it, but the the naive approach
I can just do it in solid 1.0. It's not
going to be as satisfying, but it's fine because I don't have create async. So,
let's let's let's do let's just drop the spell code in here for a second and look at how we translate this. I don't feel like doing it in a stack blitz.
All right. So, we're going to go function down here. Okay.
down here. Okay.
Never mind. Let's do this.
Put this in a fragment.
I'm using solid, but conceptually this would be the same in pretty much any framework historically. If I just take this,
historically. If I just take this, put it this here format. No, too much stuff going on.
format. No, too much stuff going on.
Okay. So then what else do we want here?
N is going to be a signal.
So we're going to make these all into signals. Can I do
signals. Can I do command D, please?
Okay, missed one. It's fine.
missed one. It's fine.
And these are going to be all signal.
And then this will be set n + one.
And then this is going to be n set n.
I could probably just have used the increment function, right? Doesn't
matter. And then we need to create the rest of the graph. We're going to keep this function out here.
And then we need to make the rest of the graph which Can I format this yet? Yeah, thank you.
And then these are going to be in solid today. It's been a while since I used those. Going to be create resource.
And then just trying to think what each one looks like. It's going to be like create
like. It's going to be like create resource.
It's going to be based on n and then it's going to be multiply and
one example right that and then all right I need to actually the value in.
All right. And we need to do that multiple times.
A B C D E
and then uh what am I doing wrong here?
Two three four, five. There's going to be
four, five. There's going to be something that I'm missing.
It's funny. There's like object object on the end of it, but like it looks like it's doing what I wanted it to do. I
just don't I'm trying to think of what the object object is on the end. Am I
there's something that I'm just because I doubt Oh, right. Create resource is a tpple,
Oh, right. Create resource is a tpple, isn't it?
My bad.
I've been so used to using create async.
It's so nice.
All right. Yay. Okay. Sweet. I remade
the demo now.
Yeah, I'm going to create async. Okay,
my point is look what happens when I click this.
They all kind of just like flicker in as they feel like it all over the place. If
I click it like three or four times, it's just kind of like oo. And you could picture if I mean this is guarded because resource has cancellation but you could picture like in a lot of
solutions if you're doing this naively like even sometimes like if requests take different periods of time like later ones might come first and like is this the UI you want? Whereas you
know solid example if I click it multiple times I mean it does jumble a bit. So, I I mean, I'm not completely
bit. So, I I mean, I'm not completely happy with that, but generally speaking, I click it once, it it kind of ties this all together in
a in a more consistent way. And it and the important part that you have to understand here is I click this, it doesn't change the 18
right away. Even though
right away. Even though even though I uh I'm incrementing it, like if you look at the solid example here, when I click it, it's 33 right
away. Like we can we let's let's make
away. Like we can we let's let's make the the base longer. Let's go like look this has to be plus 200 at least, right?
If I do that, look what happens.
It up this updates right away. But this,
you know, whenever. Whereas if I do the same thing over here and go plus 200 on this on this new spell version,
it doesn't do it until everything's finished. So it's a bit like a
finished. So it's a bit like a transition except there's there's no transitions here. And I was like my
transitions here. And I was like my first thought was like this is kind of confusing because there's no abortance.
You you think your UI is just slow. Like
why is your why is this taking forever?
Right? like you click it and then it's like oh there it is right I was worried I mean the truth is when you have transition you get a pending state back
and and spelt actually um does they have this effect pending it's kind of like a global and then this way you can kind of see right away dot dot dot and then goes in so that easy
solve I just wanted to kind of show that I I saw this model and it's interesting because it kind of looked like transitions but Um,
it wasn't. But the this isn't the hard
it wasn't. But the this isn't the hard part about um like transitions. The hard
part about transitions is actually loading things off screen and not releasing what's on screen. That's
actually the hard part. Like when we had our example here, like how is it that when I move over here, I I still have
the counter counting up like still reactive while it's literally loading stuff on this other page. Like the if if you look at this from like a pure
conceptual standpoint, there's a decision here. And usually when you
decision here. And usually when you switch from one like in a reactive system with ownership if you switch from one one place to the other place you have to dispose of the old stuff right.
Um so even if we do some fancy stuff you know it's like not enough.
It is kind of like promise all but the important part is the consistency here right you don't like this is
tearing this is all 1.0 know that I'm showing here, but this this is tearing. Like
this like you've seen this before.
Sometimes tearing is okay, but I it's there were some studies that went into like trust. We were talking about trust,
like trust. We were talking about trust, right? What if a classic example um do
right? What if a classic example um do is the movies app still up? Um
movies app. Uh
solid movies app, but I don't want the solid one. I want the
solid one. I want the Who made it?
Taste taste js was it movies app I'm only going to be able to do this once but the angular example had tearing
and I started using this as an example of tearing because as you click like look at popular here you look at popular movies right if I go to action
some of them are the same so it didn't matter but it's going show action before or adventure here before it actually updates the movies. We'll be looking at adventure and then it'll the movies come in later. This is a classic example of
in later. This is a classic example of tearing. Now, you might say that this
tearing. Now, you might say that this example is fine, but there's other places where like you don't want to tear. You'd rather show the correct
tear. You'd rather show the correct header with the correct content.
Um, you could argue that this is better than having a placeholder like like you know it could have been this scenario, right? Where if I remove the transition
right? Where if I remove the transition again, how do I remove the transition?
Yeah, remove the transition. It's better
than like every time we move. Sorry.
Yinking it out from under ourselves, right? Like you've seen apps like that.
right? Like you've seen apps like that.
you know, me switching to animation didn't yank it out. It just showed the old stuff a bit
out. It just showed the old stuff a bit longer. So, like this could be an
longer. So, like this could be an intentional design um thing. But like if I look at the uh like say the solid one and the the view one, what you'll see is
as we change categories here, we show a loading loading indicator and we we actually delay
switching until like the whole content is available. You can decide whether that's better or not. The
browser does this a bit internally called paintolding. But this is this is
called paintolding. But this is this is like um an example of like transitions. Um this
isn't this funny example because this is actually like our server component example um that never got released. But
um I think Nux does the same thing. When
I go here Yeah.
Yeah. These these are not full page navigations even though they look like that intentionally so because they they use a form of of basically holding um anyway. Um
anyway. Um well exactly what this is the question that people ask like what if what if we we do something a little bit more extreme like
make this you know let's make this 3 seconds.
Okay, here we go.
Okay, we're in. Right. That was actually really long, longer than 3 seconds. But
now when I click on it, what saved us is that dot dot dot the the pending indicator. Pending
indicator like gives us an idea of what's going on. If I didn't have a pending indicator and I just just did this.
I wonder if Okay, that just seemed longer than three seconds. You're you're
you're like, "Did I get it?" You're like clicking. You're like, "Wait, did it
clicking. You're like, "Wait, did it work? Click it again. Did it work? Click
work? Click it again. Did it work? Click
it again." And then next thing you know, what?
So like you need the pending indicators, which is hardware hard to default to holding.
These are all reasons that I wasn't so sure about this as a default. Right.
Right. I I Let's pick this apart here.
It's longer than 3 seconds is blocking.
Yeah. Yeah. Like the spelled actually can do that tab demo with this model which confused the hell out of me because I kind of understood what they're doing. They're just locking up
they're doing. They're just locking up the the reactivity. How the hell could they keep the counter counting? Right?
This was something that that confused me and I and I realized only recently how they did it is that you you need
Selt actually had uh had a async felt um uh GitHub discussion back in April and
here let's take a look here.
I think I get quoted in here. Ryan,
according to Ryan in a stream, this is unsolvable issue without explicit dependencies. Okay. How do effects not
dependencies. Okay. How do effects not run until all async drives are updated?
Right? Like how do they do this smart stuff? And and Rich's answer is
stuff? And and Rich's answer is hilarious. Uh we don't run them. I
hilarious. Uh we don't run them. I
haven't seen the stream, so I'm not sure why it should be impossible. Well, it's
impossible from a uh from a discovery standpoint. You don't know what you
standpoint. You don't know what you depend on until you actually run it once, which is why I've been big on splitting effects because then you you can and it's the advantage of like
React's model with a separate dependency rate, right? Like if they get rid of the
rate, right? Like if they get rid of the dependency arrays in React, they're just compiling them in essentially. They're
not removing them. Do you do you understand what I'm saying? Um they
still happen before. Like there's two different phases. React renders your
different phases. React renders your component and then it runs your effects after. So when it runs renders your
after. So when it runs renders your component, it actually knows what the dependencies are because you told it compiled dependencies have a whole bunch of shortcomings. Um, as we know, like
of shortcomings. Um, as we know, like what if they use functions? What if the stuff's out of scope? Of course, React answer that is memorize the function itself, which I think once you're down that path, you're like cuckoo cocoa
puffs, so to speak. But, you know, if the compiler does it for you and you don't have to worry about it, maybe that's okay. Um my point is that like
that's okay. Um my point is that like like they know the dependencies up front.
How does spelt know? Well, its compiler does it. They basically look at the
does it. They basically look at the block. They look at it within local
block. They look at it within local scope and they actually associate it with like some kind of block concept whether it's a DOM or whatever you want to think about it but it's it's goes beyond the reactive system and they use
it to really smart effect because they identify these zones almost like from the UI framework from the component level and they were like okay this is a zone that we can associate and schedule these things with they don't have to
rely purely on runtime reactivities. So
I understand his confusion cuz he's like we just don't run them because they're already collecting them in a place outside of the reactive system like they're already like in sense we had our queue but like the work that I've done
on solid has been pretend nothing in the universe exists except signals. This
solution requires a compiler. it
requires a different scope context like it it requires some frameworky stuff to work but once you have it it's kind of like an obvious answer you like just
don't run them right but it it's important to kind of understand this distinction which is why I'm bringing out this thing what I've been trying to say
this whole time is different afordances are good at different for different use cases.
The thing is routing resolving can is a perfect example of where these async features come into play because you have pages that you're looking at pages in the
future, pages that require data, asynchronicity. This is one of the key
asynchronicity. This is one of the key places solid, you've been using trans, if you're a solid user, you've been using transitions for 5 years. They've
been just baked into the router.
you the only like is pending indicator was is routing that comes out of the router. You guys just maybe didn't
router. You guys just maybe didn't realize it. Some of you did. I get
realize it. Some of you did. I get
issues every week. Do you want do you want to see? I I bet you there's one right now. Solid router. I try and put
right now. Solid router. I try and put it in the read me. Try and put in the docs. Let me let me look it here.
docs. Let me let me look it here.
The first screen falls not working with anchor. Create as defer page refresh but
anchor. Create as defer page refresh but does not work with solids routers anchor. When clicking anchor, the entire
anchor. When clicking anchor, the entire page holds until the call completes.
This is a bug. It's not. But
essentially, this confuses the freaking hell out of people on a weekly basis. I still think it's the right default. I was okay with this. I built it into the design system,
this. I built it into the design system, but I have some experience now seeing what the results of actually building this stuff is. And we've been using doing this for years.
I mean suspense is kind of like a promise all but it's a non-blocking promise all know the problem with promise all is whatever point that you put the promise all you go await you're kind of like stuck
suspense lets you collect all the stuff independently and then figure out how to kind of filter down obviously when you're like boolean show don't show doesn't make a big difference but I
think I think it's important to understand that the non-blocking nature which is important for solid which renders once you you'll see like if I console log inside stuff that has suspension you're not going to see like
the stuff that renders off screen we keep when we insert it back into the page so like our ability to act like our ability to
actually um uh how should I put it like our ability to be non-blocking behind the scenes is actually to our benefit obviously if you
have cascading fetches blocking is bad because if you await and then you have you fetch again, guess what? Waterfall. But more so um
what? Waterfall. But more so um like you could you literally could render the whole shell of the stuff like and not repeat that work ahead of time and what else are you doing? Twiddling
your thumbs waiting for async worked.
You know it's the classic JavaScript problem. So um
problem. So um it's a little bit more complicated than promise all it. It's the same reason why you don't put a weight in client side components because different state updates can happen. You don't want to
like block. This is a fluid zone like
like block. This is a fluid zone like async components can make sense in something like server rendering which runs once. You know, generators actually
runs once. You know, generators actually make a lot of sense if you think about the way like streaming works, but generally speaking, async components make no sense. Like there's too many
different sources. You don't want to get
different sources. You don't want to get it all entangled in the same thing right?
Yeah, looking at query clients is really really quite like SWR or React query is really useful for understanding the mentality here. When I showed Tanner the
mentality here. When I showed Tanner the stuff that I'm trying to show today, he was just like he's like it's like he he got the primitives right away because he already had designed them himself when
building query. Again, this is
building query. Again, this is science discover, you know, but as I said, every so often someone surprises you like spelt did, which really sent me
kind of off like they've had this for months. I didn't really care much,
months. I didn't really care much, honestly, though. And we should talk
honestly, though. And we should talk about why my perspective changed here.
I mean, suspense used to have a timeout, you know. Uh, the truth is, I mean, yes,
you know. Uh, the truth is, I mean, yes, you could do something like that. It's
it's fine. And it's just you kind of never want to go back if possible. I I I think obviously the affordances might not be strong enough, but as long as you give people the tools for affordances, I
think it's okay.
Feels like state machines I mean state machine are a great solution to to managing lots of states. State machines
are like a very way of I think you know I call it the grocery list shopping you know like when you have a bunch of procedures and you're like look I need to categorize these procedures and understand what the state should look
like at the end of each one state machines give you a very descriptive way of writing that which is fine I just think they are verbose for simpler problems when you're like
I'm just freaking updating some UI now we could say that everything is a state machine you know you can metaphysical and we can go out there and it's true.
I'm just saying like kind of like RxJS.
It's it's one of those zones that you know when you need it, do you know what I mean? Like you will get to that point, but not every problem gets to that point. And it's funny, they solve
that point. And it's funny, they solve very different types of problems. State machines are really good um you know like RxJS and events are very good at solving a lot of transformational stuff
over time and state machines are very good for um reeling in all the random um the random like state. The problem is
we're we're creating too much state in the first place. like if if we actually looked at state in a different way and I actually had like a Twitter post about
this but um I mean where was it? Um
I was saying that it was it was one of my use effect rants. I can't remember if it was after my last stream or not.
[clears throat] H um yeah because that's isomeorphic first which we talked about with Tanner
yeah here besides being the perfect use of the mean I know a lot of comments playing the firmware sometimes shift the solution foration is less obvious in synchronization but that doesn't mean synchronization shouldn't exist we should all
derive more um damn it why can't I find it there should the um it doesn't matter. Um my point is
that it's it's too many. I know it's like right around here. It's I I basically blame use state for the problem with effects.
Yeah, I'm just like not finding it right now. Why am I so bad at this?
now. Why am I so bad at this?
It's been too long since I streamed. I
think that's the gist of the problem here. It's I'm probably like scrolling
here. It's I'm probably like scrolling right past it.
Oh yeah, here it is. It's like literally the next stream. People give People give use effect a bad rap.
Let me drop this in here. People give
use effect a bad rap, but it isn't usually the source of the problem. The
real problem is too many use states. It
does be able to see how the data is connected and model it too discreetly.
As those connections appear, it's easier to synchronize and refactor because basically instead of like changing how these things connect, you're just like, "Okay, I'll just like call a set statement in effect." And once you start down the path becomes harder to refactor because you can no longer see what the
the dependencies are. So because you wrote this one and then you wrote this one and kind of it just cascades out.
You can't look at data declarations be like oh it depends on this. You will
have to find all the places you write to it and and those locations depend on what writes to those. And this isn't react only problem. Some tools are better encouraged derived state but none are immune. Yeah. And what I was getting
are immune. Yeah. And what I was getting at is state machines are like when you have too much state. If you remember on the remix three 3 stream, I'm like I said something along like it uh the
lines of like remix 3 and um um uh xate were going to be best friends.
And the reason for this is because I think their pattern even though it removes um stuff that shouldn't be state from a React standpoint like it removes like temporary calculations because
they're based on events. I think the pattern still because it's not based on derivation actually creates more state than you actually want um because it
because you start writing to it from all the locations rather than deriving it and for that simple reason you will accumulate more state quicker and um x8
will look very nice to you um it doesn't matter if it's user effect or not it's the problem is not driving it's that's the problem here the core problem it's
not the It's not that uh that you're using use effect anyway.
Yeah, I like this. You know there's not another you can easily opt out by two separate states. one for the counter,
separate states. one for the counter, one for the drive. Yes.
So, yeah, the I'm actually not criticizing Six. It spelt was actually
criticizing Six. It spelt was actually incredibly like illuminating. The thing
is I just the behavior was not what I liked and the way they solved it was one that just wasn't interesting to me because it literally relied on a compiler and it was I was just like, okay, whatever. And thirdly, there's
okay, whatever. And thirdly, there's actually a third reason. I looked at this stuff and I was like, you know, when I want to transition, I'll, you know, put in a router or something or like a specific use case like an action,
you know, I'll wrap it in our action query helper, who cares? And then
unfortunately or fortunately, depending on how you look, I had Ricky uh Hanelon on on stream and he convinced me that
spelt was right, which is a funny thing. he probably
didn't realize he did. Um, like all those critiques aside, I had Ricky come on and first of all, we all know how hard
transitions are, but having now watched or and worked through the stuff on stream, having watched the React Comp
talks, talks plural and and learn with Jason, I was Like this is so hard.
It's the kind of thing that like I find myself making mistakes on like forgetting to call the second star transition. Like I I I've done this on
transition. Like I I I've done this on stream. I I've done this like Ricky's
stream. I I've done this like Ricky's done it. We we've all done it. And
done it. We we've all done it. And
trying to explain people what a transition is. I do it on stream so you
transition is. I do it on stream so you guys are educated. But at a certain point you're just like spelt doesn't freaking tell you what a transition is.
It just kind of works. Um
um maybe not exactly the way I I I want it to work, but close. And
the the final breaking point for me was actually Let me go back here. Okay.
Let's bring back this. Okay. Yeah. So,
we're in a good place. We're back to where we started. Okay. Let's go to the dashboard. Um
dashboard. Um it's it's funny how we we pull away different stuff from the same examples.
Where is it? React 19 async.
Me and Rick made uh made it to-dos app on stream and I was I I was kind of baiting it a bit cuz I I came up with this way to basically
take the client app in solid and not change it very much to um to make it async. Um, I I basically had
just wrapped the the the stuff in the actions in a transition, kept the same state updates for optimistic and everything just worked beautifully. Um,
obviously it was a little more verbose cuz we had to figure out how to continue. So I was like using generators
continue. So I was like using generators like with yield. I showed this actually on my talk. Um,
uh, let me actually get that in here too. Why not? I'll show you like uh
too. Why not? I'll show you like uh collection yan signals talk. Uh so sync to async complete. I'm going to drop this in the
complete. I'm going to drop this in the chat first. Let me go to source main. No
chat first. Let me go to source main. No
here.
Let's just drop this in the chat.
So okay I I was proud of this example originally because I literally just took the same client side code. Like if you're just building the to-do app, you just like
remove to-do, you just call this toggle, you just call this and just wrapped it in a transition and then did the server action and refreshed the data. Like I
was like, "Look, without touching any of the other code, this just works." That
was what I was like hoping to show like and I was like pretty proud of myself.
This has been something I've been working on a lot with with solid like server functions. We came up with server
server functions. We came up with server functions cuz we didn't want you to change your code like change your client side mentality.
like that was the whole design like back in I originally had proxies in 2021 when I was first working on this kind of like automatic RPC thing and then by early
2022 Nquille came in was like let's use the compiler right um but that that that is basically why we kind of came up with server functions and
it I wanted to slot them into tansty I wanted to slot them into any existing library that existed and I felt I was doing the same thing here Um, except I
mean generator. Who here knows what a
mean generator. Who here knows what a generator is? I mean maybe people in
generator is? I mean maybe people in this stream but like and the problem is if you do an async function then you have the stupid resume thing or like because we wanted to connect them or you
have to call start transition again you know like it just it is not nice. Um
I know that Ricky knows that I built it at the router for that reason. But
what Ricky did I did not see coming was he he he knows people didn't want to call a transition themselves. So he's
like what if you build transitions into the data fetching which is like what we do if you look at solid router and our action query things the there's transitions in there and he's like what
if the the trigger is built into the event handler in the base component. So
what he ended up doing in our example as we built it is at the top of the page where we have our stuff. Yeah, we have an add to-do action where we just await and refresh remove. I mean this looks
like simple easy to follow code. And
then if we if we trace this down to find where we call that our to-do input has an add action has an add to-do action.
And if I follow that down, we'll see that in here we start our our transition right?
And similarly um if we go into each of these to-do list we have a toggle action remove
action and we even um I'll go into to-do even further. Here we go. Toggle remove
even further. Here we go. Toggle remove
action and we we we handle our optimistic state here locally as we needed and we manage the transition with it. So,
it. So, what's cool about this is if these components already exist, the developer
um has pretty easy APIs to work with, right? They just await the thing. It
right? They just await the thing. It
seems pretty straightforward, but we started playing and composing and we we used Ryan Florence's select demo as the
basis of this. Um, for those who have not seen this demo, um, it's it's just a
cascading drop down. It's the silliest thing ever, to be fair. Um, but we we're like
it it's funny how many things just keep on coming back to the stupid example.
It's not stupid, it's a great example.
It's just it's there it is.
just to kind of show you this example.
Um, oh, this is I I made a new version that added an extra level to it. Okay, the
the original demo didn't have a country selector, but it basically worked like this right?
And as you change the states, it would reload the cities and auto select the cities essentially and show the loading state and handle this. And this is like
surprisingly interesting problem because the state that you update impacts a different component so to speak, right? Like, so
this was like a good test and as we played with the composition patterns, we started noticing interesting things like wanting to force suspense into the child components. Um, it was funny. I was
components. Um, it was funny. I was
working on a version and there's a guy in our Discord who just who really wanted to test Ricky's thing. So, he
just kept building examples in React.
Tons and tons of examples of composition in React to see, you know, where we would land and compare them to what I was like working at Solid. He kind of almost like nerds sniped me because I was just like doing my work and he's just like, "Why doesn't this work? Why
didn't this work?" And I'm like, "Well, does it make sense for this to work?
Should it work that way?" I'm like, "No, that doesn't make sense." So, like I got just dragged right in to spending like a week playing on the versions of this the this demo. Um,
this demo. Um, guys.
Yeah. Yeah, exactly. Thank you. Um
Um, it was it was it was very helpful.
Uh that's how we ended up coming to basically producing what I I'm very happy with um in terms of like optimistic updates and the whole thing.
But during this process during this process of working through this demo, two things happened.
And let's see if I can find the original.
Um, I got to go backward.
I've you I've used this demo so much.
Um, it's actually kind of funny.
Uh, let me go here.
Yeah. Yeah. Yeah. Yeah. Yeah. Look,
design async select like he's this is how they're pitching you consuming this stuff. But
stuff. But you know, like where you have like action action, right? But
the one thing that came through this was a spell version.
And thank you, Simon. And I looked at it, I was like, man, like now
this isn't generalized.
At the time that there's a solution to this now, but at the time that they made this demo, if you made this a general component, you would have to do this too. And that's a little awkward because
too. And that's a little awkward because when you change it, they both freeze where you really only want the second one this freeze. The solid solution I said isn't global pending is not sufficient. It's not quite good enough.
sufficient. It's not quite good enough.
But the key part that I want you to look at here is like the solid example. He
literally just has a stack of states that are derived. Right? If you look at I I don't have the exact solid example open right now. Um this one's a little bit more complicated um because it's got
the third drop down but to add the extra drop down all I had to do was add an extra select and add the similar state.
So just states derived at the top fairly straightforward exact same logic and then he just bound the data bound the
the setter the end. No transition the thing you just set the value and like I mean props. I mean, it doesn't get
mean props. I mean, it doesn't get slimmer than this. Uh,
to be fair, I would be fine even if it didn't have the data binding. I don't like data binding, but like just conceptually just being able to pass set state in right
now. Ricky's version has set state in
now. Ricky's version has set state in passed in because literally it's uh async design component and this is value action or something, right? Like like we
saw um we saw in uh the tweet that I was showing uh Ricky showed yeah like you know what I mean
like this right he is just passing uh I think where is it oh he's putting the data fetching in
line okay whatever it that's weird anyway um I I could picture a very similar type of solution because you've masked this away in the design
components but then I started thinking I'm If I'm inside if I if if instead of on click now I'm
using action. So I'm inside the this uh
using action. So I'm inside the this uh so this react thing right so where we are I'm inside add to-do action here I'm inside here
and forget that it's adding to-dos and await but if I do anything in here if I add if I set any state or do anything you know it could be related to what I'm
doing not related to what I'm doing like kind of related you are in the transition
you have spelt's behavior if if if if like essentially like not the same I I like react's behavior slightly better but like you have basically the same zone where you're everything's like
locked up you know um if if like that's your baseline the challenge is every single component in your ecosystem needs to migrate to this now to to actually
benefit this in a clean way. Now, this
can be a slow process, a gradual process, but if this is the future and the writing's on the wall, obviously if I'm React, I can't just like pull the carpet on people. spelts
like surprisingly pulled the carpet on people multiple times and no one really cares but um like [snorts]
you know I'm small small solid small too you know and I just look at this and I was just like while it bugged me that if I could like update two different states and it' all
get tangled together in the spelt version and like like why like like Like like I actually have an example. I'll
show you in a minute what I'm talking about. I I I just occurred to me that I
about. I I I just occurred to me that I was just like if this is the future, if we're just using this instead of on click, then
we're already in this felt world anyway.
So I I had to stop and I was like the last if if we're going to go this way, the last thing I want to do is is force people to figure out the when
they have to call transition again. Like
the problem is you're essentially introducing modes, you know, like you have like even if this hides the transition, I'm still thinking about
transitions because if I put it on click or versus action, I actually have two completely separate modes of behavior, right? Like
right? Like there there's no world that even if I hide the transition keyword that I'm not thinking about transitions here.
What's the JS output between the SL and SWAT one? Um,
SWAT one? Um, I mean, if you want to see, it's funny.
Uh, not that example, this example. Um,
JS output. What you're going to see is a bunch of compiled async. Yeah, look.
Promise all right here.
It's not. It's promise run. But like
they're compiling in stuff and organizing your code based on your async behavior, right? And then dollar sign
behavior, right? And then dollar sign async promises get this and then they like it's very much into the system, right? If I find the solid version of
right? If I find the solid version of the this original demo here, um not the more advanced one with the
triple. Sorry, where's
triple. Sorry, where's Yeah, whatever. I'm gonna have to keep
Yeah, whatever. I'm gonna have to keep track of tabs here in a minute. Yeah,
this is the triple one. I I love the triple one just on it side. It it this is But um if I look at this one and we look at the disc here, this is all the
library code as well. So I I can't quite play around it, but let's start from where our actual component starts right here.
Template code. Okay, that's fine.
If you notice the stuff above the JSX has not changed at all. It's just create async signal say in sync signal then create component suspense loading children do. You won't see anything
children do. You won't see anything related to async. So basically the reason it's solid version is completely runtime. There's no compiler
runtime. There's no compiler augmentation. There's no blocks, no
augmentation. There's no blocks, no promise all like it's it's it's completely like there's it's not analyzing your code in any special way.
It's just the classic, you know, create some render effects, create some components kind of thing.
So fine, I found myself 0% converted. Is
that a word? Well, I'm not I want converted to what? Like you I I I'm just putting the I guess the question is people are are they a I haven't really
gotten to whether the async features are worth it. And I'm probably not going to
worth it. And I'm probably not going to get into this into in the stream because this is one of the hardest things to show because Oh, you don't find it controversial?
Okay, that's that's fine. We haven't got to the controversial stuff yet.
I mean there's a little bit of controversy here, but you can kind of see spelt and reactor agreeing about something and
by doing a a little bit of a shift of mentality of what's first, you you kind of end up in a in a in a fairly different place, which is kind of
interesting. Like it's similar, but it's
interesting. Like it's similar, but it's different. Like I was talking about how
different. Like I was talking about how in this example um in the React example here once you're in here you're in the transition. Well, React has a solution
transition. Well, React has a solution for that. They have special optimistic
for that. They have special optimistic state and I think that's still a great solution because basically you define the quality on the state itself. You have state and you have like
itself. You have state and you have like optimistic state right and depending on that it behaves differently so that you can just call the setters you want in here. So everything can live in the
here. So everything can live in the world. That's your that's your trick.
world. That's your that's your trick.
Like before they used to have used deferred value and stuff and you do these it was actually more complicated and you do these things where you'd have to write something outside the transition and inside the transition essentially if you wanted to do like a
type ahead or tearing or something create optimistic or use optimistic. The
beauty of it is you can do the set in the transition which lets them compose it like this. My so it completely works.
There's no shortcoming of just making everything like a transition like this.
The only shortcoming is the refactor cost which is something you that you have to do as React but something a smaller framework would have to like it'd be more expensive probably on the
like because people haven't really opted too much into all our async features. It
it'd be more expensive for people to probably uh refactor cost um than it is to change the behavior here. So someone
asked earlier if if I saw a bunch of breaking changes. The challenge is it's
breaking changes. The challenge is it's not API changes, it's behavior changes.
It's like when React changed to calling set state after it's it's it's it's we can keep the exact same API except for
splitting effects essentially. Um
but but the timing changes which I think is arguably harder to detect and actually bury breaking. So it's just the but all the simple demos will like the
90% case will all just work the same as it always has. Do you know what I mean?
like it's it's just the last 10 become really hard to narrow down which is scary.
But you won't need to refactor most code if it's written in a good way which is an interesting dilemma.
The thing is yeah t sorry dev response tack is acing in the component. Yeah, the thing is most of the things here are about async
entering the system. It's
how should I put it? Our systems still are set up to be mostly synchronous perceptually, but kind of like how you have async await which kind of gives you that illusion that you have a continuous flow. That's what we're looking at here,
flow. That's what we're looking at here, right? Um but we haven't got to the
right? Um but we haven't got to the solid solution yet. Um to be fair, so I I I I need to continue I guess.
Yeah, I mean caching can be part of the solution but mechanically at a fundamental level in order to get these features that you know you like stuff like Tanstack should actually be tapping
into React's render model. hard because
of like they have their own storage considerations.
But this is why stuff like transitions and suspense exists so that third party libraries can just tap into it and streaming just work. Like we had an incredible integration with tanstack query where like literally you could
just go into solid start put tanstack query in use that as your datalay layer instead of like our other stuff and essentially streaming just worked instantly because it was built on our premise switch to handle the
serialization we had the building blocks already there so everything just like it was literally just like no thought and I think that like that that's why even with local
person all these things the async primitives in the framework are incredibly important because that's the interfacing point like tanstack query
like how does how does it get from promise to state we help it right that that's on us okay so like so
far like you now you're kind of like at my starting point and I'm sorry it took almost freaking two hours to get there but um Like
I I was pretty convinced here. I was
like, I have to drop everything I'm doing to look at this.
Like if this is the future, how could we look at this any other way?
Like tricky.
I knew there's parts about spelt solution that wouldn't apply to us. I'm
not going to rely on a compiler. Someone
actually came to saw Discord and was like uh you know today or was like talking about compiler stuff and I was like no not not going to rely on compiler but can we do something different than what React's doing here?
Right. That that that was my kind of baseline. Um let me look at my notes and
baseline. Um let me look at my notes and see where we are on it because we kind of jumped around a bit.
Okay.
Okay. I know where I want to go next.
Okay.
Um, we did talk about what transitions are and why they exist. Does anyone have
questions about why they exist? Like why
it's valuable to be able to hold in the past. Um,
past. Um, I'll leave this one.
You know, like I think like it's easy.
We understand optimistic and showing the future, but that only really works on mutations because you know what you're going to change.
We we all understand tearing because we've done this a bunch, right? Like
it's just kind of the default. You fetch
it in use effect, right? like here
terror it's 3 * 15 is not or 3 * 16 is not 45 but it's going to show that for a second you know especially if I slow this down more
right 3 * 3 is not six um but sometimes we do want to tear right so that we can have a a loading affordance we understand placeholders suspens is
easy. The hardest one to understand is
easy. The hardest one to understand is hold in the past and why it requires such complicated solutions like transitions like
do you fun do you fundamentally understand hopefully what I was not this example um
what I oh did I already close the tab example down it's fine I'll do one here like why I'll solve it. Yeah. Why transitions
are are important here. If we want to like continue to have See, I don't have the counter on this example, but why we want the continue have this page live and updating while we load the next page off the other side of the screen. Right?
This is not easy to do. Somewhere
there's a conditional that that literally discards the one side and renders the other side. Having both
exist at the same time requires consideration.
Yeah, I mean it depends.
The problem is this is a trivial silly demo. Picture this was like a
silly demo. Picture this was like a shopping cart with prices and line items and like an invoice.
and that stuff took slightly longer to to update.
This is not what you want. There are
cases where you want tearing, but this this is not this is not one of them. Uh
Ricky has some great examples that he that he showed like a few weeks back on Twitter. I I covered them in previous
Twitter. I I covered them in previous stream and it's like when you kind of put them side by side you're just like you kind of realize like for me this realization actually happened when I was
working on React Native back in uh uh 2016 time period I was building a production app in React Native and um it was just kind of like the difference I
was looking at our existing native app and I needed to get the performance so I spent a lot of time performance tuning but it wasn't just that it was when I was looking at um
like the affordances and the way it work this holding kind of pattern and last dirty state was super common. I was used to the web like like I don't know if I can see it on Twitter right now, but I was using to the like
like all these like spinners and stuff on the web, right? Like you just saw the Twitter spinner and native wasn't like that. everything was smooth and animated
that. everything was smooth and animated in. Like I think the thing is once you
in. Like I think the thing is once you get the holding stuff, see the the when you have all the spinning, it's fine to have placeholder stuff like spinners and stuff, but when you have that, you can't
really transition the uh UI as much.
It's very common in native to have content to content, which means that you get the smooth looking animations. So
holding is actually kind of like part of the equation to to to get that like and animations in terms of transfer. It was
just when I started seeing those kind of things and then when I did the kind of like transition test with people and their perception of performance and speed actually even if it was the same speed or slower they thought it was
faster because of the way that it was like less intermediate states. Um, like
there are times and places for tearing, but I I I think I was okay with it being an often feature to be fair for the longest time, but
it's freaking it's really confusing because I think I think the reason it's confusing is because the dec like where to stick it in the chain is really hard.
Do you stick it at the the root but then it's not your decision or does every place that you want this kind of behavior need to like be opted in and then like what if something else comes related to
it gets like [clears throat] what mode you're working in becomes like at least with the the API that that Ricky put forward you're like I am in an action I
know that I am in this zone like there's a mode switch um free-for-all is kind of scary right and the problem is you think okay well what if I just zone the UI
like like say like this is consistency zone like the way we do suspense this is something that I I considered for a while too especially when we were working on the Marco team and it was like the problem is transitions are
global it's like it it's like inverse uh kind of situation like it's the things that don't get caught by suspense you know what I mean like it's the it like
technically what we there's like a mechanism in here where new suspense um actually goes to fall back and like so you can
break out of of transitions by using suspense. Um,
suspense. Um, but like it's the topmost level. So zoning it
doesn't make a ton of sense because you you can't you have to be able to provide the guarantees at the top at the level. Like
if someone wants something to be consistent, that is a global thing. Um,
you could opt into tearing, but opting into consistency doesn't really work like from a like a zone style. You opt
into consistent like with a transition by wrapping the whole action from right all the way to end. Not from a zone of the UI. It just Yeah,
the UI. It just Yeah, hopefully that makes sense.
Um, where was I? Um,
sorry. Let me grab my notes again. I
kind of got on a impassioned thing there. Okay. Yeah. So, I think
thing there. Okay. Yeah. So, I think I think we talked about transitions and why. Yeah. I I think I think I'm hoping
why. Yeah. I I think I think I'm hoping people kind of get it. Um, but that is a great question. I'm I'm glad you asked
great question. I'm I'm glad you asked because I I spent so much time like thinking of like every random way like okay we don't need transitions if we bake stuff only into the router. But even when I was
trying to bake stuff into the router it this this even if you like kind of manually manage the fork yourself of the next page. You
need it to go from being like special version to being the version and like swapping it in and then everything ends up flowing through like this top level
API interface like it it just didn't work with any level of nesting like so I was like I tried to avoid transitions for like a year uh because
they're in one solid one and I was like man these are complicated they're buggy even in solid one they're they're buggy I'm like I didn't want to go there again and then I implemented them completely
ically one way using forking of the graph which is like my ideal form that I thought like what I'd learned like in solid 1.0 0. When I first released it,
we had transitions um which kind of just added some extra transition fields onto the signals, but the graph shape kind of stayed the same, which was awkward cuz then I had to like make this custom code
to around disposal and like trying to figure out what I was doing. And around
1.3, like the 3 months after uh solid one came out, I started working on like a a better uh approach that actually cloned the nodes and did forking. I got
like decent place. Um, and then Milo kind of took over. Um, and I kind of moved on to work on like other features that I needed to for Solid. And then
Milo disappeared for a bit. To be fair, he's in he was in school. Um, but when he came back, he's I'm like, "So, how did the transition stuff go?" And he's like, "Oh, I stopped working on that and
I built a a different signal library uh called Reactively that does everything lazily and blah blah blah." Like, which was good because that kind of triggered
the whole like signals thing in 2021 like the or um I guess was it 2021 or 22 like when Preact and all that came out
it was like right after Milo was doing all that work um and uh essentially that solution never worked and then I
just remade one of that design that actually worked and now I'm talking about as I said a fourth version to these these uh transitions
thing that doesn't make sense to me is if you have a state graph that can be independent of the view react can why not have the view be the thing that it's holding optimistic etc well
consistency of data flow and cascade of like like to be fair I we don't you're right and in a sense it is the
view that handles it in the latest model that I that that I'm working on. Like we
need the like the blocking behavior to prevent waterfalls. But if you actually
prevent waterfalls. But if you actually look when we get into my new solution, it's only the render effects that are special.
So yes.
Oh, come on.
There we go. Sorry. Streamyard sometimes
freezes up some kind of instate graph.
So I think there's actually different path here. It sounds kind of like zoning
path here. It sounds kind of like zoning you mentioned. I don't fully get the the
you mentioned. I don't fully get the the issue here. Well,
issue here. Well, it's cuz for something to be consistent, it has to be universally consistent is kind of like the problem.
Well, transitions don't need async local storage, but to be fair, actions still have the same problem like like when
you're doing an a mutation because it's not tied to the graph. But we'll we'll get there. Okay. So, this this is good
get there. Okay. So, this this is good because this let's let's talk about consistency for a bit cuz we've already kind of talked a bit like one of the key parts for transitions was splitting the
effects because I wanted to be able to run and know the dependencies up front so that we could like make decisions especially in the case of lazy systems be able to pull and know you know like you've hit all the async stuff and that
it actually is because because you could be your effect could be under a suspense boundary and not trigger the transition.
Like there's ways that you can opt out.
you actually have to wait until it propagates down to the effect which means you have to run the effect to actually know and then by then kind of you know it's it's too late if you push it off. So you actually that's why split
it off. So you actually that's why split effects you know have become a thing.
But um what I wanted to actually talk about is about consistency in general and and we should I wrote an article a while ago. Let's see if I can find it.
while ago. Let's see if I can find it.
Consistency cost of consistency in UI frameworks.
Maybe not nearly as popular as it should have been but who knows.
This is a I made a very simple example in React.
We've seen this before. State memo ref and then I set some state and I went log log log log and I made this example in
view. I made it in spelt three and I
view. I made it in spelt three and I made it in solid and we all got different answers.
React was 000 because it doesn't update it right away. Vue was 120 because it updated you. It got the most recent
updated you. It got the most recent count and the most recent double count, but the DOM hadn't updated. Spelt was 10 0. Spelt updated the count, but this is
0. Spelt updated the count, but this is before it had signals. So, no way of having the drive state until it ran the component again. Like spelt used to be
component again. Like spelt used to be almost like a VOM until version five.
And then Solid had all the dates up, all the numbers up to date because it was the same. It was it actually just
the same. It was it actually just flushed right away when you did account.
It just unless you opted into batching solid I basically made a hedge when I made solid that that because we were fine grained enough like the updates
wouldn't be that expensive and if you cared about it you could dispatch. Now
dispatch. Now this is an interesting thing because over time solutions have changed. spelt now is the
same as Vue here with spelt 5 and solid 2.0 know what I was working on until um you know a few days ago is actually
the same as you as well. We all kind of collided it just we're like it's okay we understand we had to acknowledge that there was like an inside and outside the system which is something that you know
is worth kind of talking about. As much
as I love one 1222 and 000 because they're both consistent, 0000 causes people to throw pull their hair out apparently, you know, because they're
like, I set the count. Why is the count not updated? Right. And um one 0 is just
not updated? Right. And um one 0 is just weird. Although at one point in this
weird. Although at one point in this article I'm like maybe we just go with it because it's like it's it's like kind of native. You set the count, you see
of native. You set the count, you see the count, but then you keep the other stuff in the past. But I was really debating through this because in solids batches we worked like react essentially
if you used a batch you got 000 um and my conclusion for solid 1.5 when I finished with this article sorry did I did I did I post this in chat did I
forget to post this article in chat let me let me see if I oh no I did I did I posted in chat my conclusion in the end was that solid
after this changed was 122 view 2. And
when you're inside the batch, it's the same as view 120. We would do early work if you read like basically if you never read double count, you know, here we would defer the work until the end of
the batch and do do it then. But if you didn't early read, we we knew from the notification of the signals that it was dirty, so we could we could do the work ahead of time. That's that's kind of
essentially where we landed. Um because
batch in Solid used to be like this and it used to drive people crazy. You know
what else is like this? transitions
because uh there's a the reason for this because of like async but generally speaking um the signal world has moved to views
model 120 and reactive state is 000 right and I I talk about this like there's ways to get the other numbers in the other systems and
people have their different opinion some people were like temporarily invisible changes X consistent with lazy recal like this Jyn who made a mole or whatever
um uh was like views the only right way and I think a lot of signals people felt that that way but yeah see he said solid's consistent but
instant recalc isn't optimal like there's no winning this battle I I mean temporary invisible changes is that a problem I don't know it depends really
put into emphasis that in all these cases the reactive system because of of the smart like how it renders and how it runs in order always saw the same thing
in all our systems the reactive system always got ran once and got the most update to date values by the time you get to doing the DOM you have all the right values in place and it like
there's no problem there this only matters inside your click handler and or maybe inside of your use effect callback back. But essentially, because the
back. But essentially, because the reason that that's the reason why this article came up, solid batched our effects automatically because we didn't want to like freaking it doesn't work to
be all jittery when you're in the process of queuing running your effects queue. And we had this 00 behavior which
queue. And we had this 00 behavior which basically drove a lot of people nuts because they'd be they'd set the signal and then they read the signal and like why is this not updated? And it was really confusing for them.
Part of me knew that making this decision might be a mistake sometime down the road, but this was like it was
like an okay concession, you know.
I mean, 1 1.20 is easy.
like it's the most obvious thing to do with signals. The problem is one 120
with signals. The problem is one 120 um the problem isn't that one two is easy.
The problem is that um and not even that it's inconsistent. It's that when you
it's inconsistent. It's that when you add async, it can no longer be guaranteed, which I'm going to get into this in a second because I I
let me show you.
So, we're now all one 120. And then
let's let's look at this. I'm going to show this example.
I remade our example installed. And
let's add the console in here, too. I
didn't do the DOM ref. We all know the DOM's going to drag behind. We don't
care. So when I click this one, two console log one two. Not surprising.
Yay. 510. We get it right now. The most logical thing for a
now. The most logical thing for a signals library is is to have this behavior. We're used to it. And then but
behavior. We're used to it. And then but let's throw a wrench in here. Let's
let's make double count drive. It's now awaiting to do it.
Now I'm going to hide the console for a second.
We know that spelt will hold it. So
we're going to see a second, half second, and then it's going to show the stop like as you expect, right?
Sweet. That makes sense. But look at our logs here. We're back to one zero like
logs here. We're back to one zero like we had before. Um, let me just toggle this again. Okay.
this again. Okay.
And what happens if I click really fast?
Oh, 1 0 2 0 3 0. I mean it makes sense.
We don't have the async value. We can we we can only show uh what we have, right?
So the and the point is this is pretty obvious that this delay is happening here but we we already saw from our uh our other example that because like you
know we're holding state or other drive state like if there was a a an async triple count like drive count like if if there was a chain they would all get
held as well. I I mean maybe that's a better like maybe that's a better example.
Let's let's try this double count.
Let's let's do this.
Did I Did I screw something up?
Oh, they're warning me that there's an async waterfall.
Does that make any sense? There's no
waterfall. There's only one await.
Let's put quad count in here.
Okay. So, this this deres as far as it can go. I I suppose if I move or if I
can go. I I suppose if I move or if I move I guess the better example is if I moved it here and then I remove this here.
And like let's pretend our event is not aware of double count. Someone like like you know what I mean? Like I'm just saying we added a weight somewhere and now
we we're not aware of double count in our component. We only got quad count.
our component. We only got quad count.
Okay.
We click and we're like oh what happened?
My point is if you depend on this behavior of derived inside your event handler or or whatnot like pretend it's
not adding a number. Pretend you derived something like length and you removed an item. You removed the last item. So then
item. You removed the last item. So then
you you look at derived pages length and you try and index a value that's missing in an array and it actually crashes on
you. This is possible, right? Because
you. This is possible, right? Because
because because adding async compromises your ability to show derived values early and it could be somewhere downstream. It could be
like I I it's hard with a simple example to to do this because it's like, you know, like it's right in front of you.
It's a simple example, but like it's it's it's possible that the difference between this this being double count and
count is the difference between you getting zero for your number you care about or you getting the correct result.
And it's especially confusing when count is updated too. So like
I I there's only really one way to think about this. It's it's that
about this. It's it's that there's a difference between being inside the system in the reactivity and the rendering and the components and being in an effect callback or in an
event handler. These are different
event handler. These are different zones. So it's not different modes,
zones. So it's not different modes, different zones.
Right. I mean the what you end up doing is you I don't know does spell have an unsettled or like a a weight tick or some something like that. You end up you
the only thing guarantee that you that you you basically go um I'm just going to let it resolve out and then I'm going to do it. Right. that there's there's a
there's another mechanism swelt has where you can ask for eager but you can't you can never get what you don't have right this is just the reality of async
this would happen with solid today but if you added a resource in between it it would yes this is I'm not picking on salt here I just want to comment here
so you kind of start understanding how Async um sort of throws a wrench on your expectations. Now, don't get me wrong,
expectations. Now, don't get me wrong, it does not impact any of this logic in here. Your component, your actual like
here. Your component, your actual like way you wire the graph, the way you build out like your UI is not impacted by this. You have perfect guarantees.
by this. You have perfect guarantees.
Everything runs exactly as you expect.
It only impacts stuff outside the system, right? And I I think this is important
right? And I I think this is important to understand because like there's consequences. Do you know what I mean? Like you
adding async somewhere if you don't account for that here
can cause to error to crash.
You could separate this in space, have the async somewhere else, and this code can error because someone changed a dependency or added a dependency or did something.
Signal isn't the place for async. Well,
it I mean, I'm I'm not saying that. I'm
just saying like this async if you have inconsistency you can cause stuff to break. Uh do you want to know like want another example? Um
sorry give me a second here. No, not in here. Uh
here. Uh um let's see what it Oh yeah, I copied some code.
This is just illustrative here. So,
let's not like I'm just going to copy this into a empty playground so we can just look at this code. This was dropped on me uh when I was in uh
on the Discord and you can see what I'm talking about. This is some solid code.
talking about. This is some solid code.
Don't don't worry too much about what this is. But the idea is you you got
this is. But the idea is you you got signal, you got some async, you fetch some value, and then you choose to show the button
based on whether the value is set. So initially
it's zero. So value is zero. So there's
a button that lets you do something with the value otherwise not. Now my question is person clicks the button right sets it
to one and this takes some async time.
Now we're in this universal transition world where the stuff is all, you know, still present and alive and stuff. And
so this is still true even though this is no longer true. And someone calls this button. They click the button
this button. They click the button inside. What is what should value be?
inside. What is what should value be?
Should value be one or should value be zero?
Uh, I I you're you're asking the right question.
Yeah, you these are all fine things. You
could say wait for it. I'm saying but your existing code has to be aware if someone goes and this this has been one of the most interesting challenges with
doing this is we're creating seamless async yet that only works if people don't have to refactor their existing code
generally or it's not that they might have to refactor and this is fair on your point well you should you know put the weight or whatever it's they might need to refactor their existing code but if they build with best practices. The
addition of async should not cause code to have to change that is because it's too permissive within the graph.
You can sync with a resolve helper. But
yeah, I I just wanted to throw this example. I want us think thinking about
example. I want us think thinking about this right now. single wrapping promises is an
now. single wrapping promises is an option because then you could like always force the waste in but that also has some interesting consequences in terms
of like API shape because now you're taking signal of value or promise of value like generically from a component comp composition to be fair I've been
seeing a lot of that happen in React but you all know I mean this is this is my own constraint I'm putting on here I want it I want Uh I've been working on
on a system where async is colorless. So
that is one solved but but you know in this version this could be a async or
not async you know but you have to know to do that. Yeah.
But the TypeScript tells you, yeah, the trickiest thing is you can do that inside the reactive system. That's why
the reactive system has no problem. But
in an event handler, you're no longer tracking. You don't rerun events.
tracking. You don't rerun events.
They're they're not expected to be reactive. They're kind of outside the
reactive. They're kind of outside the system. I I I'm I'm doing this on
system. I I I'm I'm doing this on purpose because I I want you I want people to see that there is a definite inside and outside the system. There's a
zone in which the inside the system which works seamlessly beautiful. The
decisions on the outside are all messy because the the reality is we live in an actual like the JavaScript is a
different world than what is inside our frameworks. Now React has always been
frameworks. Now React has always been very simple. like it's our components.
very simple. like it's our components.
That's our magic zone, you know, where we where we control and have different rules.
In our case, it's our our reactive graph, right? In Steel's case, it's
graph, right? In Steel's case, it's wherever we choose the compiler to touch.
Um, but like the it it's I just wanted to kind of like I'm I'm not necessarily what the right answer is, although I suspect that this
should say zero here. That's my my gut feeling. But like th this is the kind of
feeling. But like th this is the kind of consistency thing. And if for this to be
consistency thing. And if for this to be zero traditionally or at least as a default then you really have to think
back to the only way this is zero is if this is zero right which means I mean surprise. Do you think react
thought of this issue 10 years ago? I'm
willing to bet they did.
we'll end up with manual fine grain reactivity. Well, I mean this is the
reactivity. Well, I mean this is the thing, right? If if you really really
thing, right? If if you really really need this capability, you're going to be calling flush, you know, or await flush, right? Like which I told you that remix
right? Like which I told you that remix would come up on here. you'd be calling update you know if if you really need to know you'll be like my gut is if you
write code in a certain way you shouldn't have to worry about dried values so the thing is whatever you set you have right here like wherever you do the set you have the value set so you don't need to read from the signal again
in most cases there are a couple exceptions uh especially around like control inputs and stuff and there's a solution for reading the latest value without having to wait on it but for
most things you are.
If you really need to see the result of your work, you're going to call flush or call update.
Rick can not hear confirm. Yeah, I don't I don't know. He He said he'd check it out, but maybe he'll check it out later.
So, transitions and forking. Well, I
haven't got into that. I The interesting thing is spelt doesn't fork.
I mean, they have a fork feature that they just showed off on blue sky the other day, but spelt's transition behavior doesn't fork. They literally
just hold the effects based on the compiler with the DOM zone, and they they must handle their cleanup in a different way cuz I think it's cuz their conditionals
are built into their templating language. like if blocks aren't a
language. like if blocks aren't a generic reactive uh mechanism. They're
literally like their if and each are literally like blocks within their like UI framework. So they can they hold
UI framework. So they can they hold based on blocks and spelt basically uses the fact that it knows that it's rendering UI and using its advanced compilation techniques to accomplish
this which I said isn't very helpful because solid doesn't really care what you do with the signals.
Yeah, I'm I'm not Yeah, I'm not I'm not sure. Um,
sure. Um, so yeah, he's not Well, the interesting thing is it I I that I am actually arguing that
that is the case. Inside the system, it doesn't matter cuz inside the system, if something's not ready, it throws. So
when you're like and it doesn't throw the whole component, it just throws inside the graph. That's if you if you've ever seen my uh um let me see if I can this site. If you've seen my
beyond signals like nested fetching example in solid, I I literally just have a component A render component B
and fetches its own data. B fetches it own data and render C and C fetches it own data and renders its own information. And these all happen in
information. And these all happen in parallel because it's not blocking. We
only block the expression where it's read. So B fetches it renders throws in
read. So B fetches it renders throws in here but still can render the rest. C
fetches throws in here renders rest.
This is like native to the solution.
It's funny. Uh, spelt actually recently released the same demo, but and I was like again I I was like he called it out of order rendering or some like fancy
name and I'm like isn't I I was confused by it because I was like like for us it's just a property or reactive system because we're fine grained. It's felt
because they tied their async to the blocks they actually had to come up with a really like clever solution using their compiler to actually accomplish the same kind of parallel behavior by like analyzing and figuring out how to
slot it. for for this. If we look at the
slot it. for for this. If we look at the actual generated code, you're what you're going to see is
like create async, read it down here, create async, read it down here. Yeah, there's some like
down here. Yeah, there's some like element walking, but literally no special case code. This is literally just how the reactivity works. And
So called a time travel didn't this your version not small world sync asynch this is probably the smallest it can
are there visualization how the graph lives while updates um uh solid dev tools does that it actually shows the signals and then you can see I I I I would love to see if we could like link
in the IDE it would be really cool to like when you like trace like what causes this the update or whatever ever. It'd be be cool.
Anyway um what what I'm Yeah. What [sighs] kind of Where are we still? We're talking about Yeah. talking about this consistency
Yeah. talking about this consistency example. Ultimately,
example. Ultimately, this is where the controversial part is, right? Because
right? Because essentially I was saying that like splitting the effects
maybe React was on to something.
There are cases though async still blocks the commander can't help there.
Yeah I yeah I believe so. Uh I I I don't I haven't created those yet, but it's just like this is a kind of difference with building into the reactive system.
It's so fundamental that it can't work any other way where if you use a compiler, it's a best effort optimization.
Um sure, but man, who wants to write them? Yeah. Um okay, so
them? Yeah. Um okay, so I I I became very aware of this when I was working on um when I was working on on some of the the
when I was playing around with this new async signal stuff that I still haven't even got into showing you all yet, but isomorphic We'll talk about that.
Sorry, I have to jump a little bit in time. Um,
time. Um, the worst part of API design is when the answer is the one your users will hate.
They will never thank you for it. They
will misunderstand it until the end of time, but there's some comfort in them never knowing the pain they may have faced. And people actually asked me for
faced. And people actually asked me for example, so I gave two two examples.
splitting tracking from depths and their side effects, not applying the value immediately after set state. I've always
been able to argue the correctness for these, but chose differently for convenience. But what happens when
convenience. But what happens when ambition grows in convenience turns liability? This was me talking about
liability? This was me talking about this because the truth is with async involved, the only way you have any guarantee is to
delay the way React does, right?
It's it's kind of just one of those unfortunate truths. It's I don't think
unfortunate truths. It's I don't think it's actually debatable.
I'm only talking about outside the system, right? Outside the system can
system, right? Outside the system can have different rules. Like we could technically you could do like like what Sel 3 did and only update the initial state and not update the deres. But you
have to understand that the derived states may not update. And this example that I showed here really really
suggests that as a default you probably should um also keep the set state in the past as well cuz this is what happened in
transitions. If you remember if you have
transitions. If you remember if you have a transition and you set some state inside it if you try and read that state outside the transition it's still the value it was before it changed it like this is intrinsic. So if you remove the
transition wrappers, remove the boundaries, then you you're basically always in a transition.
So it kind of makes sense. It has to operate that way.
Well, deferred updates can stop some of the throwing issues even in handlers. Is
still possible someone reads from an asyn?
outside of the system.
See like an in effect the dependencies are tracked, right? So you can you're not outside of the system. It's only if someone triggers an event handler that's off screen
basically.
Convenience turns liability is the name of the spelt four to five album. Yeah,
definitely. I mean I think React is definitely sitting there and going like told you so a bit like especially when spelt changed uh you know a bit you know
the funniest thing is like it's not so simple like react compiler is has a lot of similarities to spelt 3 so like but see react doesn't really have to
worry about a told you so moment unless someone convinces them to switch the signals which I don't think is going to happen and we'll look at a bit in a No, this is the same model that I've
been talking about for ages with the throwing. The thing is initially,
throwing. The thing is initially, initially when you're rendering the UI and you throw, it doesn't have a value. Afterwards, we
can treat everything as in the past.
It's only initially that this is a problem.
Um and I'm kind of banking that in the majority case, the data that you would read in an event handler is the same data that you
would have in your view. Like show this thing with this data and then it's like do something to that data. I mean, it's not the 100% case, I understand, but it's something that I'm okay with. I I
know we've been debating this for ages, but um as I go farther, I become even more convinced.
You could start coloring the async everywhere but it it doesn't the cost of that migration is huge.
There's even React kind of like even though they're talking about passing promises to a certain degree the reason you have these mechanisms is because we don't want to be awaiting everywhere.
Now it's possible outside the system that you have no choice right but inside the system you don't want that.
You saw the error insult when you didn't read the async data in the template it crashed rendering. Oh, really?
crashed rendering. Oh, really?
Oh, that's what that happened, huh?
Yeah. I don't know.
Okay. So,
I told you this is going to be controversial cuz because obviously I'm saying from an API standpoint, maybe React was right. I knew that people weren't paying attention to this post
even though it got 120. So, I did a poll. I was like, if I had an approach
poll. I was like, if I had an approach for async that requires no rappers, no compilers, no non-blocking, and render, no coloration, just works, but it requires set single and drive values to stay in the past until flash like React,
would you take it? And don't don't let don't get me wrong, this was a bit of a litmus test. Basically, there's only one
litmus test. Basically, there's only one wrong answer to this poll.
Um, you know, I think saying no, Async is not worth it is perfectly legitimate. I
I respect you for having that thing. And
I think if you're this person, it means a lot less React users responded than I than I would have thought.
Yes seems good means that a lot of you guys just trust me, which makes me feel good.
No, React is the worst is really the only wrong answer. Um, so this was a test.
Fortunately for you all, it was it was anonymous, but it's interesting. Do I
see the same on blue sky?
Uh, okay. Where was it? Where's my
profile here? Profile. Yeah, I did the same thing on Blue Sky.
People on Blue Sky seem to like React more. There's less votes, but it was it
more. There's less votes, but it was it was interesting.
There's a lot of react hostility on Twitter maybe, but it's just kind of funny. Anyway,
funny. Anyway, um almost the exact same ratio of people who trust me and yes, used it. More
people who say async isn't worth it. I
just thought it was interesting.
React is the worst is always right. No,
no, that that is not always right.
Then baby Ram took a bunch.
Yes. Yeah. I think it's but the people are dramatic with the bias but even the real answer is async isn't worth it.
Yeah. So I I do have to consider that that I think saying async isn't worth it is a very respectable position to be in.
I just it is very interesting when react and spelt think async is worth it. Then
again like I I don't expect Vue to come around here but Vue is hard. it view is hard to judge because they're rarely like on the cutting edge of innovation
so to speak like DX stuff happy users Angular 2 like very good at taking care of their developers but not where I usually look for for ideas
react very driven to the future and so it's spelled as of recently so definitely worth taking notice This what do I mean by set signal? I mean I
mean this set signal. I mean like if I call this and then I go console log,
right? So if I go here and then I go
right? So if I go here and then I go console.log
console.log value what is this?
If the answer is zero, that means it's staying in the past.
But if you stay in the past, that's bad, too. You click a button on a new page
too. You click a button on a new page and act like you're on the old page.
Well, internally, it doesn't stay in the past. It's only It's only the basically
past. It's only It's only the basically the user interacts with what they see.
is the way to look at it. Internally,
the system can get ahead of itself.
It's just that if you if you're showing the past because you're like holding or in a transition, what you interact with is also in the past.
Oh, yeah. I'm missing a second. It's
fine. It's not a working demo, but yeah, I'm missing a second. Thanks,
Right. The immediate
Yeah. The the immediate thing it's is easy. And often
easy. And often you see no getting the future value is easy too though.
You know Do you know what I mean? Like
you could do the same thing. const new
equals to set value new console log you know I mean new is a JavaScript keyword so I can't use it but
you you get [clears throat] getting either value is easy in the scope. What matters is when you're no
scope. What matters is when you're no longer in that scope. Like like in this example, like when you're over here,
right? And I don't think like
right? And I don't think like generically we I mean you could do try and be really fancy based on what flush it was on and like say like this click handler comes
in at a different time. So like this is part of this like it's possible a little bit. Yes.
Yeah. You you need to be able to say like do this when I'm done. But again
it's okay because it's outside the system. Inside the system everything's
system. Inside the system everything's seamless outside the system. You just
need a way of saying like, hey, when this is done, continue. I'm going to set this up and
continue. I'm going to set this up and then when it's done, I don't know how many cases are actually where that's actually necessary
cuz a lot like you have two options. You
can basically do the stuff all up front and use your intermediate values that you have on hand and then you don't have to wait till it's done.
The only time you need to wait done is like when you're like reading from the DOM or like controlled inputs. Like
there's there's situations where I can I can see it. But
transitions don't have isolation levels.
Now, yeah, I mean, we could talk APIs in a minute, but you you conceptually get get that the I'm really trying to double down on this outside inside the system
thing.
Okay. So, where am I next?
Yeah.
Okay. Well, then I I'm actually ready to actually get into the the main topic now.
I mean, have you seen any I I I mean, maybe this a good refresher. The demos
that I've been doing for the last little while actually show this off quite well.
um graph power.
Just a quick refresher on create async before we get into the new APIs. Um
because this is because you're creating a graph, we can still use stuff like error boundaries to catch errors.
So like it propagates along the graph because the whole thing is a graph. So like if you if create async errors in the same way that async propagates errors
propagate and then phrase errors.
So phrase errors then upper phrase errors. We don't care if these things
errors. We don't care if these things error so much unless we're reading them in an effect right unless we actually reading from them. So if you read from them in a
them. So if you read from them in a render effect an error boundary will catch them. So every so many clicks this
catch them. So every so many clicks this is going to error out.
It's random. So give me a minute there. Aer out. Right.
there. Aer out. Right.
What the cool thing is because we familiar with the graph.
This is kind of crazy to React developers because like this only renders once. It's possible for us to
renders once. It's possible for us to like obviously when I click again, we can update the signal and it might not error the next time and then that signal will get all the way down here and render the right thing, right? But
what's really cool is this retry button, which literally it's just a reset coming from the fall back. It's no clue what's going on. It just knows it's errored.
going on. It just knows it's errored.
Well, when it resets, it actually tries to pull at the nodes. It kind of goes, "Okay, well, this node's errored. What
what what's going on?" So, you click retry, and it, oh, it's errored again.
Click retry. Oh, it succeeded. Because
it it was able to trace the error graph and goes, "Okay, you're errored. So, I'm
going to recalculate your parent. You're
errored. Going to recalculate your parent. Oh, you're async and you
parent. Oh, you're async and you errored. Well, there's no solving that.
errored. Well, there's no solving that.
Let's fetch it again.
So, we're able to use the reactive graph to inform uh based on the error.
Simple. I mean, we have the the we have the committed the pending value. So, in the case of rights, you're inside a a setter. inside
the setter um we will give you the the um eager appending value because you need it for like mutation you you need to be able to like push something then add something and do that. So inside the
setter you will have full access to the eager value. Now you might be like what
eager value. Now you might be like what if like what if it's dependent on it it's it's tricky because what what if
it's dependent there's a certain point where like you could be writing to um like it can't give you the eager value if it doesn't exist yet. Do you
know what I mean? Like it can give you the eagerest value it has, but it can't give you it can't give you the it's possible that if something's in
flight and you write to it while it's in flight that the thing that comes when it finishes overwrites what you did, right?
It can only grab the the value that's latest available to it, but it will try inside the setter to grab the the latest value. And that way if you have multiple
value. And that way if you have multiple mutations like you're pushing or adding like something in a store that will still work. It'd be very weird if you
still work. It'd be very weird if you push to a store and then access an index that should be there and isn't there.
I mean this is create async signal create async. It's it's derived. I mean
create async. It's it's derived. I mean
unless you mean like we you can always drive from it to make it mutable but there's no real point to okay but anyways what I'm getting out is errors are just handled naturally
through the graph to their source where either you have a user effect where we can have like an error handler um it's a as a callback or we or a render effect
which gets caught by an error boundary.
Just sort of aside, but this is kind of this basis hasn't changed in the last several months. I'm very happy with the
several months. I'm very happy with the way the graph translation goes. But
[snorts] let me pull up my hackd here.
We did cover this on the last stream.
It's been so long on GitHub.
Okay sweet.
Yeah, I covered this in the last stream, but I want to refresh people on this a little bit.
All of this led me to saying what happens if you design an async first framework right?
I this started as an idea, but I had to drop what I was doing to test this idea, right? So, first of all, we talked about
right? So, first of all, we talked about this everything would be transition transitions implicit, not recurren API.
You'd opt out of async into tearing like we do with create optimistic, but the default would be always on. This means
no click action. On click would just be a transition and even the backside of create effect. Okay. Right. What I'm
create effect. Okay. Right. What I'm
saying is if you want to have tearing in this world, you might need specialized state. But what was interesting for for
state. But what was interesting for for me is uh couple realizations. You only
need optimistic state for data you don't have yet.
Um because this was the break point when I figured this out. You can determine what's pending state by just asking is pending. Wouldn't apply to things
pending. Wouldn't apply to things downstream from the async portion of the change. But anything from the start of
change. But anything from the start of the change that hasn't settled set the next tab but can't see the change yet is pending would return true for it. So
what I'm saying is is pending just means you haven't committed a value. It could
be because it's async. It could be because it triggered something async.
But and once I realized that this was the case, it it became very very unifying from an API standpoint. like I
started already seeing this in my my other examples, but um
uh let me see if I can find an example of this. Um probably in my
of this. Um probably in my uh solitude experiments might even be in the tab example. I don't think I have it open anymore.
um experiments tab.
How do I show the pending in this example?
Because I'm using the global transition here. See that? I'm not using like use
here. See that? I'm not using like use transition. I'm using is pending and
transition. I'm using is pending and look where is pending is is pending tab.
I'm literally I'm not asking about the data that you're fetching. I just know that when I set this tab like I'm like if this tab is pending then the like
this is always the problem right if you I I created this pending originally to be like oh is this data waiting like is the graph blocked you know and that was cool because you could ask about
anywhere especially it would allow us to like not go back up to suspense you know and we could just tear and ask this question but the problem is like how do you show is pending for something that's
offscreen right the data fetching off screen.
How do you make that decision? And what
I realized was when I did this async first thought thing is as long as the value isn't committed like you're in a transition, then you also know that you're pending, right?
And I changed uh solids transitions to be able to ask that question. So it's
not just the the stuff downstream from the data. the stuff that triggers it
the data. the stuff that triggers it part of that kind of unit you can ask is pending about which means that now you have a unified API you can say like look you could be in your component and go
look is this pending you don't care whether it's locked in a transition you don't care whether it's because literally the data isn't there yet you'd literally just go it's not ready you can
you can defer the you know you can handle the loads indicator which is really cool for Ryan Florence's demo
which we we will get to Again, what the trick to is pending and yeah, no more set pending. The trick to
is pending is it's a graph question.
This is I love this because it's not on each signal. I mean technically in the latest implementation there is an under the hood that's on each each signal that gets read under this context
as shallowly. Um but this implementation
as shallowly. Um but this implementation is done differently that doesn't do that. Like it depends on the
that. Like it depends on the implementation. That part is not
implementation. That part is not important to deal. You can ask it about a bunch of things. It's pending this or it's not like and doesn't matter cuz
it'll and or this you know data like you can ask this about any expression in a typical solid manner. We don't want is signal right. I
manner. We don't want is signal right. I
I don't want signals to have a special API. I don't want you to feel like you
API. I don't want you to feel like you need to wrap something in a memo or computer signal to be able to ask a question about it. I very very much dislike making signals special. You
should be able to ask it about props coming in, right? You know,
props.options is it pending? You know,
yes, you can ask this question.
Okay, I just shortcuted it because it's already a function.
Will there be a special node like? No.
No.
Right. So I I'm literally just asking this about any expression. You can go is this pending. Now I'd already kind of
this pending. Now I'd already kind of gone down this path, but I when I was designing this, I was like, this is the key. This is the key. Spelt's local
key. This is the key. Spelt's local
thing doesn't work for composition. They
need this. They will probably add it.
I'm not surprised. like effect pending you. If they could have just gone
you. If they could have just gone disabled pending um cities, disabled pending states, like if they could just ask it about the
specific thing that they cared about, you wouldn't have this problem, right?
You don't want to lock this up right now. This is your only choice, right?
now. This is your only choice, right?
When you change it, guess what? You
can't keep changing it. If this takes a long time, you can't like change your mind.
No. Why?
What do you mean is signal? This is not a signal. It's literally the opposite of
a signal. It's literally the opposite of is signal. Is signal is saying that
is signal. Is signal is saying that something requires a specific wrapper. I guess your concern is that
wrapper. I guess your concern is that people will be checking for async but you will be checking for async anyway
because you like disabled like you you you're look if you build it okay I mean we can look at this in as first
principles I built this API um if you build an API uh I'm trying to think is is this one a better example yes this one's a better example I built
this API I and I just did the default thing. I didn't put pending state. I
thing. I didn't put pending state. I
just built this API. And let's go down a little bit from from this. Okay, let's
go down a little bit from this. And I
click on it and I'm like, geez, this is taking really long to update. What the
hell's going on? And you're like, oh, it'sing async, of course. What's the
first thing you do? You go like, oh crap, I I need I should show a loading indicator. So then you go and you show a
indicator. So then you go and you show a loading indicator, right? That's the
first thing you do. And in my opinion, you like in your head you go, "Oh, I should like it's not the initial load, but like it's the update loading thing." So you're like, "Oh, effect. pending." Like you're
you're going to go, "Okay, how do I show that it's pending? It's like the first thing you reach for and it's usually tied to like that disabled button or something." It's it's the default is
something." It's it's the default is you're like, you know, if you miss something, you're like, "Oh, this is weird." Oh, right. It's loading. And
weird." Oh, right. It's loading. And
then you just It's not It's not a identity question. It's like you do it
identity question. It's like you do it for a purpose to show an affordance.
It doesn't look the same. To be the same, it would need to not be a wrapper function. If you put a wrapper function
function. If you put a wrapper function in between, it wouldn't work, right? To
do is signal, you would need to like you would need to be able to pass something directly into it without a wrapper function. So you could like look at it
function. So you could like look at it like this this API is not a signal, right? Because a signal um
right? Because a signal um I mean I I don't even know how you get a signal out of this. I guess you would like internally
intercept all signal reads and then like but like it it doesn't even make sense, right? Because a signal would be a
right? Because a signal would be a single thing. Are you saying is they are
single thing. Are you saying is they are they all signals? Does there exist a signal in this? This whole API doesn't work for is signal.
Yes. And props. Wouldn't work unless this was like was literally a signal on the end. Like a getter wouldn't work.
the end. Like a getter wouldn't work.
Yeah. Uh kind of like that. Like depends
on the implementation. I've done two completely different implementations, but yes.
Yeah, we do tag it on and we're like you're part of this transition and then yeah, in any case for composition place and localization,
I think it's super important to be able to ask the specific question like like is it are you waiting on like if you're making a select component, it's
very obvious. You're like you you the
very obvious. You're like you you the thing that you might care. like disable
the select list if if or make show loading indicator if I don't have my options to choose. That seems like something that's very generic that you could just add to a select component.
Anyway, [clears throat] latest isn't a thing anymore. You need
the opposite. Every value is the equivalent of latest helper in this world. So, you might actually want the
world. So, you might actually want the opposite. A way of showing the proposed
opposite. A way of showing the proposed value before it's finally applied. Edit.
Spelt is proposing an eager helper to do this. Makes sense.
this. Makes sense.
Um, in in my solid examples I'm going to show today, I didn't really have a name for it. I call it pending. So you have
for it. I call it pending. So you have is pending and pending. One gets you the pending value. One asks if it is
pending value. One asks if it is pending. Same API works. You know,
pending. Same API works. You know,
pending also um is an expression. It's not you can go
an expression. It's not you can go pending procount. You know, it's it's
pending procount. You know, it's it's it's the same thing.
Can it be a third function in the well that's why I'm saying I because you're asking about the graph not about async. You could be asking it about upper phrase which is derived from
phrase you know what I mean like this shouldn't be something that you put on the primitive itself. It's very
important because otherwise people will do weird things like wrap their async with other async so they can like get their hands on it or something like it's
yeah I mean yes but I mean it's a naming thing I I did I I tried I mentioned eager and people like that's weird and I'm like okay whatever. So I just called
it pending props account. We we have that as well. So there's is pending and pending, but it's pos possible that people think pending means true false.
Is it a pending value? So I'm I'm okay.
We can debate what to call it. I I
called it latest for a couple days. I
wasn't really sure what to call it, but yes, there is one that returns count and there's one that returns true,
but yes, it's the same idea. State
changes would apply async. Yes, React
behavior. Since derived values take time to propagate and you wouldn't want to witness torn state, it makes no sense to commit state immediately. More state
changes could occur before synchronous block ended that could cause async to entangle. So you could never know at the
entangle. So you could never know at the time of write that you commit it right away unless optimistic.
And I keep in mind I wrote this before I even had any idea if I could solve this.
This was just I love starting with an idea. But I did recognize that if you
idea. But I did recognize that if you weren't going to update state right away, Milo's R3 looks really interesting. And
for those who aren't familiar, I talked about this on stream a while ago uh Milo Pro reactively. Where's R3? It's funny.
Pro reactively. Where's R3? It's funny.
All his commits are probably on R3, but Oh, he's doing some dev tool work. Where
where is R3?
Pause it backtory R3 19 hours ago.
Optimistic is I want to be optimistic is different. Um optimistic is state that
different. Um optimistic is state that doesn't exist. Like in React these days
doesn't exist. Like in React these days they're using it for all the cases. But
I I don't think I think we should keep optimistic to refer to stuff like when you it's not like it's like stuff that doesn't exist like you manufacture like when you're saying like add when you add
a to-do optimistic lets you add a new to-do to the list before you actually get the to-dos from the server where this is more of like if you change the tab
to from A to B a way of getting B even though you're still showing A optimistic as I said has a very very different like
purpose in terms of like it's it's not like hiding something that's under the hood. It's literally like it's the React
hood. It's literally like it's the React uses it for both but that's why in a lot of React examples you have to call set state twice. you're like set pending
state twice. you're like set pending true and set whatever, right? Like you have to set both you sometimes like you end up setting doing double setters where here
you don't actually have to do double setter.
You could use optimistic. I'm not
getting rid of optimistic, but you almost just don't need it for these basic data fetching cases. It's you
probably only need optimistic when you're actually performing actions.
Oh yeah, good question about errors.
Yeah. Um,
state updates aren't really rolled back.
If Yeah, it's about eventual feature. If
something errors, the new state is shown with the error boundary. But don't you roll back the signals? No.
Like if you change to tab two and fetching two fails, you just show tab two with an error, not roll back to tab one. Yes.
one. Yes.
optimistic state gets rolled back. Yes.
Yes. That's the that's the difference.
Okay. So, if if we're if we're going all in and we're not going to do updates right away. I I rejected R3 originally
right away. I I rejected R3 originally because it's eager like solid is today and we're all lazy. But the funny thing is while I
all lazy. But the funny thing is while I was working through solid 2.0 like 90% of the stuff was eager anyways. Um but
what inspired this was modern single implication Jasc implemented using pushpull push tryoling. However, this
approach requires overeager marking of all recursive dependency making impossible to be an 01 implementation of create selector create projection like things that project like uh do um
derived granular updates um which marking only a subset of the children based on the return value of a computation. This library implements a
computation. This library implements a different approach to activity using topological execution height ordering to allow for nodes to dynamically change sorts of the heights. We fall back push pull push algorithm. Okay, but I'll
explain what that means in a second. But
essentially what this means is he he's created an O to the one reactivity algorithm which I I benchmarked it. I
mean it's similar to alien signals on most tests and actually faster in it than others. He might have created the
than others. He might have created the fastest reactivity system to date, but it didn't fit our use case. So I kind of rejected it.
Yes. Uh I already have examples with optimistic uh the to-dos example that I was showing earlier here. I mean this one's in react but yes
here. I mean this one's in react but yes I I have a version of it.
Collections solitude experiments. No,
not that one. Collections
yan singles talk async to complete. What
you're going to see actually is an optimistic store, which is really a cool little thing because I was able to take the same logic I had in the original store and make an optimistic store. And
then the way this works is it's derived from the async data. So whenever the server refreshes the data, it just automatically does a reconcile dip on the store on the optimistic store here because this is actually a projection
with the readr um because this is a function. I I'm I'm kind of hiding it.
function. I I'm I'm kind of hiding it.
But in the same way that we can do derived signals, we can do derived stores that are projections under the hood. So this is actually this
hood. So this is actually this technically speaking. So um and I made
technically speaking. So um and I made it so that um diffing is the automatic default for returning data. Uh if you want to do
returning data. Uh if you want to do mutation you don't diff anyway. Um so
then basically you have create async and then you have the store which is writable. So for ephemeral here changes
writable. So for ephemeral here changes it tries to do these changes. If the if it fails then it will not apply them and it'll you know you can like the
transition like basically it these always reset back to the base value. So
if it fails your div refresh won't have the new data which means that it will be diffed out of the final solution but if it passes the diff won't find any changes and therefore do no additional
work. Um but the optimistic state
work. Um but the optimistic state generally gets reset back to what it's derived from um at the end. And in the meanwhile you can do fine grained
updates like push or filter or update one of the to-dos. So you get fine grain updates on the optimistic state um and then diff on the way back that hopefully matches the optimistic
updates and no changes. This is like how you do the Strello demo um with minimal code.
Yeah, you missed that part. I It's for actions that we could have a different API, but generators are you either have to use a generator to reintroduce the
transition context or you need to um have async context. We talked about this earlier in the stream and I talked about in previous streams as well.
Yeah, it's it's just it's one of those nastiness in React. Right now you call transition and then you call transition again to wrap the data refresh. Um it's
just we can build APIs on top of this like the current query and action APIs in the router. So developers don't need to
router. So developers don't need to worry about that. And this after the await literally happens inside the logic of the router action you know with the server functions doing the validation
automatically. But this is the low-level
automatically. But this is the low-level API which is really cool because it's generic. It literally works by basis. If
generic. It literally works by basis. If
you were to build a nicer wrapper over this, it's it's literally this code. Way
simpler than what we have to do today.
It's just part of the basic um system.
Okay, sorry. Um got tangented a little bit. Um I want to talk about R3 for a
bit. Um I want to talk about R3 for a bit. Uh
bit. Uh just a just a second here just so you can understand what's going on here because um
I mean I did talk about it a little bit before if you see this from the stream.
I was talking about um one of the challenges I hit because I did talk about this article last stream. Um but
essentially with a pushpull system when you write to the signal you have to notify and then when you access a pure computation on demand it'll calculate the value or it'll basically run everything when it's when it's running
the scheduled side effects. It'll pull
at which point it will calculate the value. In React, you call set state,
value. In React, you call set state, nothing applies, and then you commit the state and do the pure computations um at
and the at the same schedule time you do the side effects, it's scheduled and R3 is kind of the same where you they don't have to notify up front because it's a
single pass algorithm. Um which means that um it can basically just do this in a single um run, which is really interesting. You might be asking
interesting. You might be asking yourself like, how the hell does it do this? And
this? And yes, it's funny. We talked about a lot of this stuff before. Um,
did I actually talk about I'm actually kind of interesting if I talked about if I talked about R3 before. Let me see
if I can zoom any further out.
No no no.
No, it doesn't look like I've talked about R3. Interesting. I wanted to have
about R3. Interesting. I wanted to have Milo on and talk about R3, but it's kind of kind of interesting. The the gist of of R3 is it uses height ordering. So
whenever you create a dependency, your your height becomes so like as you create the the graph, your height becomes one plus your dependency. So it
just as it runs the the the function it goes okay this dependency is level one this is level two level three and then it goes okay so my biggest dependency is level three I must be level four and
then when you go to insert instead of it just being like uh pushed to the end of an array you actually put it in at the height so stuff tends to run in the order that it was like created in from a
dependency standpoint now obviously with dynamic dependencies it's possible for stuff to get out of whatever. But what
Milo realized is if you ever come across um a value that has a height that is um lower than your height, like so if
you're level four and you you hit an if statement and you read a level six, well, and then you're like, "Oh crap, um this hasn't been processed yet."
All you need to do is mark the nodes from from your current height because you've already run through everything like you you've already run through level one, level
two, level three, you know, you're on four. You can just mark the remaining
four. You can just mark the remaining nodes that are already queued in the heap with the classic pushpull algorithm and then color them and then then you go
six are you dirty? like so you can fall back and then once it's finished and it goes six goes oh um yeah I am dirty in fact so I do need to recalculate my
value then you are no longer four you're now seven and the next time you run you'll be seven and even if that dependency changes and um there's no
more six like three is your highest dependency you'll still run at seven it doesn't really matter as long as you run after your dependencies so it's it's kind of a clever way that it deops in
the specific case one time in order to make sure that stuff's up to date with the marking like so at worst it it's basically like worst case it works like
alien signals but best case it can work better um so like it's very very cool approach but the downside is because it's lazy you can't just ask a value any time you could but then you'd have to
recmp compute the whole heap graph which to be fair is not that different than what solid does today when we you call set state and we just run the whole thing But um
you can understand why it starts looking more appealing in a world where um where your like react and your state changes are in the past because then you
don't have to sorry this is a good time to show that Excel draw then you don't have to worry about about this. There's
a second reason why this is really attractive and that is the way we do transitions today is cloning. We clone
the reactor graph. You can think of it like get. We literally go I I I think I
like get. We literally go I I I think I tried to maybe not in here. Yeah, it
wasn't on stream. I didn't do it. It
doesn't matter. I tried to to like show what this looked like. I think Dev tried to show what this looked like. It's it's
very hard to show in space. I think I wrote an article explaining how to solve transitions that I covered on stream before. I showed like cloning part of
before. I showed like cloning part of the graph and like committing it back in like get and that's expensive. Can you picture if
that's expensive. Can you picture if every time you set state you had to clone the clone the graph especially when you have to consider like we make real DOM nodes not just
virtual DOM nodes it would be very expensive um like the we don't have to clone the cloning doesn't make new DOM nodes the rerun does so that's like not exactly
fair but if if every time you set state you're going to have that overhead of cloning and then merging it back in like it's okay when it's like isolated to a transition but literally every operation
would be had um it'd be expensive. I
mean, JS framework benchmark, I think there's a version in here of React with transitions, which again is completely useless in this example. It's
literally just a run through, but if we go transitions, React cooks with transitions 1.93. Like
we're talking like not quite knockout in this example.
Where what are we looking at? Like
uh incremental DOM. Is there anyone else in this? Are we are we No, we're better
in this? Are we are we No, we're better than Ember. But yeah, Ember is here.
than Ember. But yeah, Ember is here.
Knockout's there.
Like this is not a happy place to be when you consider that React itself is the why.
That's so funny. Why can't I scroll?
Okay, React itself is um should be ahead of React, right? React
say faster than React.
React.
Where is React?
Oh, yeah. No, I'm still seeing React libraries.
Yeah, React hooks is over here.
Yeah, React classes. Yeah, base level React is here.
Hand optimized. I say base, but hand optimized. The compiler is right here,
optimized. The compiler is right here, basically beside it. Basically the same speed. Compiler hooks right here. Yeah,
speed. Compiler hooks right here. Yeah,
it's like 01.
Add transitions needlessly on every operation and you're like way off the deep end. Like it's not what you want to
deep end. Like it's not what you want to do.
So how do you add transitions essentially without paying the cost because you don't want to fork right and this is the challenge if you notify you have to fork at that
point because you have a different state you you're basically like I mean maybe there's a way you don't have to fork but that's the point at which in my transition thing I forked because at that point the nodes are
entering a new state they know that hey I need to be updated reality you don't want you don't want them merged Because like what if something that's not in the transition updates and then you know
like then it's like in the wrong state or what if something outside the transition sees that it updates and tries to recalculate and it calculates without the transition value in mind and
then clears that state and then the next thing the transition sees and it doesn't see it. Like you this is when you fork.
see it. Like you this is when you fork.
Whereas if you have something that doesn't notify, you can basically defer forking until
you when you actually run the thing, right?
Brain hurts. Sorry.
[sighs] Yeah. Height is how many times?
No, it's the height. So, it's it's it's like um it's like we could pick a graph shape.
Maybe this is the one where I did it before.
Let's pick a graph shape. Yeah, here we go. I did talk about this is it. This is
go. I did talk about this is it. This is
it right here. Height zero, height one, height one, height two, height three.
It's based on the shape of the graph.
your height is always one plus um thing you depend on at least. So like it's possible when this starts at zero when this one gets created it goes okay I'm
bigger than this I'm one this one gets created let's say and then it goes I'm bigger than one it becomes two but as it's like I guess that wouldn't be the order when you when you do creation
order you you you don't run three before two anyway so creation order almost always works like because generally um you you like you might have created
these out of order And these might be created in a different order than this, but you're not going to run this one until you have both depths, right? So
that's actually fairly straightforward, but it like this could be create signal, this could be memo, this could be memo, this could be memo, this could be an effect that reads from both of these.
That's what I mean by height order.
Does that does that make more sense?
And then in the future when you cue it, you you cue this at zero, you cue these at one, you cue this at two, and you ceue this at three. And then when you run through the graph, you run it in
this order. 0 1 1 2 3.
this order. 0 1 1 2 3.
And you did this all without having to do like a multiolors thing like a Yeah. Like if you've ever seen uh
Yeah. Like if you've ever seen uh supercharging reactive performance, which is Milo's article.
Um, go away. Um, oh, right. I forgot the stupid images all disappeared. I got to remember Milo, which is unfortunate.
This is the only one that's still here.
But like this coloration algorithm where it kind of walks through and figures out what has changed or could have changed.
Generally it starts with you do an update and you flicker everything like these red and these green as in these have definitely changed these might have
changed and then as you walk through you kind of flicker the state but you end up like if you notify everything like this and then you cue the effects
and you do it lazy from poll you actually start I mean I don't have all the images here but you actually start from the bottom essentially and then you
go you go okay am I dirty Maybe. Am I?
Check E. Am I dirty? Maybe. Check C.
Definitely dirty. Run C, which causes E to run.
And then F continues on, sees D. Or no,
now F knows that it's dirty. So F starts running. Um, and it's got it's got E and
running. Um, and it's got it's got E and then it sees D and then D is like, "Oh, I don't know if I'm dirty, but I could
be." Checks B. B runs, then D runs, and
be." Checks B. B runs, then D runs, and then F runs. That's the state-of-the-art in signals frameworks these days. That's
not how Solid 1.0 goes works. Solid 1.0
was really simple. You literally just push these all on a stack, run these ones before the effect at the end. You
just push it. It was always eager. These
were always scheduled. So, in a sense, R3 is a return to Solid 1.0's simplicity in terms of propagation. Um, but it's smarter. So, like it doesn't like in
smarter. So, like it doesn't like in solid 1.0 it was possible.
My my little baby has just managed to open the office door. I have no clue how he did it.
>> You didn't close it all.
>> Okay.
Very impressive, Luka. Daddy, very proud of you.
Okay. Um,
you can find the complete model on Do you have the link for that? Uh, let's
see. Milo, I guess you can't post it on uh YouTube. Milo
uh YouTube. Milo MG blog because I actually don't know one boat project.
Oh yeah, nice. He has all the images here. Okay, this is the version that we
here. Okay, this is the version that we should be using from now on.
Luka did a little set open true there.
Yeah, I have no clue. Oh, he got in, but he's crawling around now.
Well, let's Yeah, you are jumping ahead a little bit. Let's Let's look at what I realized. Um I It just R3 seems more
realized. Um I It just R3 seems more more attractive now in in this kind of world where you can, you know.
Um anyway, uh let's continue.
So like because you don't have to flush early. That's that's basically the
early. That's that's basically the thing. Um and then
thing. Um and then actions need to be different. Blah blah
blah.
I talk about deferred set signal and saying how everyone would hate it. and
is pending is a good idea and we should do that. That's where I left it last
do that. That's where I left it last stream and I kind of walked away and I was like whatever, right? And then I had
the uh a kind of breakthrough um like a it occurred to me that we could just take a completely different approach.
Now I've shared this article on Twitter, but I want to cover it on stream here.
Um, no. Um, he's literally working on
no. Um, he's literally working on something right now. He's kind of disilling it to the world.
Yeah. I mean, is it true that the overhead push pull thing is sometimes more simply than pushing? Yes. Uh, it
would be right. Um, you know what? Maybe
this is a good time to take a quick side tangent before we go into this. Um um
because No, no. Well, let's finish this thought
No, no. Well, let's finish this thought and then we'll Yeah, I I want to watch Joseph's talk on stream and talk about it a bit. Um which is completely
relevant to this. Um
but yes, the pushpull ultimately reduces the amount of work on the end. So it reduces like the user code. Um essentially it
means that less user code runs which it makes a lot of sense, right? Cuz like
everyone, you know, you hear people complain and they're all, you know, they're always like talking about React and performance and stuff and and uh and uh and like
I I think it's kind of funny because like the React team's like, "Oh, this kind of performance, framework performance doesn't matter. It's your
end user code that bogs you down." Well,
it's actually pushpull is what reduces the end user code that actually runs.
The React compiler does it as well, but I want to point out that but the cost of the push pull isn't that expensive. But
if you do like an very myopic like like the JS reactivity benchmark, you're probably going to see the cost of that push pull like where you there's no load on the end, no actual work, and you're just measuring how fast reactivity
propagates. Something that propagates
propagates. Something that propagates once through is going to propagate faster than something that basically does two and a half. Um, so
compared to any actual work on the end, this is why I usually ignore those benchmarks. Um, obviously
benchmarks. Um, obviously there's like a nice benefit to knowing that your system's generally simpler and faster. Um, but
faster. Um, but it doesn't matter. The the reactivity benchmarks don't matter nearly as much as the JS framework benchmark or and even that has like a limited scope of
use.
The problem is if you don't do it smart push like push without having this kind of smartness in terms of or scheduling like signals do like like RxJS or events
can lead to tons of wasted work at the end. Tons of wasted work because they
end. Tons of wasted work because they like create cycles and you know diamond problem and all all that kind of stuff.
So like you if there's definite benefits to push pull over push um in terms of like making sure because
it's like it's push knows what's updated but doesn't know doesn't know what you care about.
Pull knows what you care about but has no clue what's updated. Right? So like
you these are the two extremes right?
Like if you have RxJS and you push out these things, you have no clue if you're going to need all that. You're going to recalculate everything and then find out that you might not need everything. Um
and and this is the danger of a of a pushbased system. Uh if if it like
pushbased system. Uh if if it like doesn't know that it needs stuff, this is why we do like push pull. Pull like
React has no clue what's updated. It has
to literally diff everything, right? it
knows that what you care about like the part that's in the view. So, a lot of the techniques that we've been working on is kind of combining that knowledge like using the knowledge of like what's
subscribed to in the graph um along with you know the kind of push pull thing.
Now you might be thinking well then doesn't R3 have this problem that push has of doing extra work and it does.
Technically speaking, you will calculate memos that might never be consumed like computing, which is the same as solid today. The thing is because we're
today. The thing is because we're focusing on UI graphs and context and ownership, it it doesn't actually matter that much
outside of like large global store type scenarios where you have lots of derived work. Um, so we were working on lazy
work. Um, so we were working on lazy memos obviously for that reason, but I think there's a better abstraction specifically for the lazy stuff be because with inside the components and
things you generally your components will render um um
like you you'll you'll create what you need as part of the hierarchy of the tree. So, like, yeah, don't get me
tree. So, like, yeah, don't get me wrong. I'm just saying on one hand, you
wrong. I'm just saying on one hand, you could be like, "Oh, yeah, lazy is way better, right? Because it doesn't do the
better, right? Because it doesn't do the extra work." But on the the other hand,
extra work." But on the the other hand, what you could say is that this is no worse than solid is today. In fact, it's better. So, like it's already an
better. So, like it's already an improvement. Maybe not as improved in
improvement. Maybe not as improved in one dimension. It It's already better
one dimension. It It's already better than what we do today.
R3 makes sure that that it runs in order so that those effects only run once.
That's the thing. The algorithm using height mapping of the dependencies informs it.
Right? So that that's that's the magic of this R3 um repo approach is it's they he
basically figured out how to use an 01 like propagation to get the guarantees of a pushbull system.
It's the only thing is it's it runs eager not lazy.
Yes. overhead of checking dirtiness of depth of the pull is often which is why React's common knowledge is like don't overuse memoization because the checks are actually more expensive than just
running in most cases. React compiler is pretty liberal in not applying memoization to a lot of like simple cases. It means you can miss some and
cases. It means you can miss some and you sometimes still have to use memo but generally speaking this is like why it's dangerous to
overuse memos.
Okay. So, R3 pretty cool. Um,
so this this I I was like, let's let's let's take a gamble. Let's build a future. I I was sitting there, not
future. I I was sitting there, not catching, should I? I I I just had a brainstorm, like an epiphany at one moment that I was like, I can do this without a compiler. I can like I can do
what spelt does without a compiler with react guarantees and and then I was like let's just pick up R3 and see if I can use it because R3 actually fits the pattern. I rejected R3 like not rejected but kind of pushed it
back like 4 months ago. I was like now's the time. Let's just build take R3 and
the time. Let's just build take R3 and build a new reactive system from scratch. I know it was brutal cuz I
scratch. I know it was brutal cuz I literally just put the stamp on and I was like about a month ago and I was like sweet I think we're done client side. I think we're ready to go to the
side. I think we're ready to go to the next phase of Solid 2.0. know alpha I have transitions working optimistic states everything seems to be good it seems to be bugged thanks everybody in the discord helping testing this is
ready to go forward and then you know I saw spelt I saw Ricky stuff and I was like and I was like
it occurred to me like I already knew we'd be have to defer the commits like the values but it occurred to me that we could defer disposal and this is this is the this is the crazy crazy part Um,
right.
I wanted to kind of show this so people can understand. I'm using solid um split
can understand. I'm using solid um split effects here to kind of show it, but but I mean this is actually talking about the problems. I don't know if I want to
get into the the the problems too much, but essentially the the core basis of it is any decision in a reactive framework like or at least
a pure reactive framework like an if statement is a memo. Um you you essentially uh
you essentially have like a I think actually I have no I didn't someone when I changed it that I actually updated it. Yeah I I don't have
that example at the moment so forget about it. But essentially it's a memo.
about it. But essentially it's a memo.
It's like a if condition go one branch and the other branch and and like I can I can draw it. Let's just go here. But
like at the very basis, a show component is is essentially like create memo.
Like you can kind of think of it like show is more complicated than this. But
like at the basis of show component is create memo return create memo um
if condition which what was like so show what was show it's uh show when yeah so if props
when like this is a way oversimplification But what I'm getting at um
props children call it as a function. Let's
pretend it actually. No, it doesn't need to be a function. Um
unttrack it looks like this.
Unttrack because you don't you don't want the children's like top level reads. So you want to keep component
reads. So you want to keep component rules working otherwise basically return
null. This is basically what a reactive
null. This is basically what a reactive decision looks like right and it's it's simple whenever when changes. Now you
might want to have a boole like what we do in show is we actually make it so that like it's truthy because this would be like when two three four it would just keep on recreating this thing every
time. So technically it's more like it's
time. So technically it's more like it's more like cons and then you go like there's different modes but it's it it's
more like this because you want to isolate um equals create memo and then essentially
what This does is whenever props changes, it checks its truthiness and only if it changes from true or false, then it will trigger this memo, which
then will rerender the children. Okay,
so there there's a show component. Yeah,
whatever. I don't Our show component doesn't have props. Right now, I mean, you're right. It takes two seconds to
you're right. It takes two seconds to add props fallback and trash props fallback. Okay, there we go. We we added
fallback. Okay, there we go. We we added we added um a fallback to our component.
There we go. Sweet. So now we've just built the show component from scratch.
Now that's that's the gist of it. Um
the challenge is the way that Solid works is it has automatic disposal.
The the problem is people we don't know the answers until we try.
I I don't know what how else to to put it.
I think it's better to do the work up front and make decisions on that and be open about it than than to release something and then change our mind
every, you know, couple months, right?
Solid 1.0 has been stable for years because I spent this kind of work up front before I released it.
Solid start took a while because we spent the work to make sure the right patterns. I'm convinced on those compat
patterns. I'm convinced on those compat patterns by the time I was at the end.
We we we waited longer and look what happened. Now it's both spelt kit and
happened. Now it's both spelt kit and tan stackar basically almost straight up adopted our patterns. Like sure there's some syntax differences but we we we
arrived at the right place.
Sometimes the stuff takes time and that's just the reality of it.
you can call it a research project but there is definitely research involved right like I I don't know how how else to to to
explain it how like right hopefully the next we will have faster majors after this because the way we're reorganizing and stuff going but um I'm I want to be confident on the
foundations we set here for this is like our React 15 React 16 moment, you know, where we have to decide the fate. This
this is the last time we get to kind of like even though the APIs probably aren't that breaking, but this last time there's a few I this is the last time we
really get to I think things just go up from here. Um
people feel I feel like people want a major version increase yet sassive when they're breaking. I mean what can you do
they're breaking. I mean what can you do right? I mean they keep on using solid
right? I mean they keep on using solid yeah 1.0 for a long time.
Yes.
And the funniest thing is this has been going full circle. I don't know if you guys are picking up on this. I I haven't talked about the solution yet, but both the eager reactivity and the the way
that I do transitions are more like solid 1.0 than the stuff that I've been doing on 2.0 the last year. It's it's
actually kind of crazy how we've come full circle. Um we were right about a
full circle. Um we were right about a lot of the stuff in 1.0.
Anyway, my point here of showing this simple control flow um thing, you get why this is here. is the force true or false so that like when it stays true it
doesn't cause it to undo. The the
challenge is inside this proc you're going to create other signals and memos and stuff or fall back. And the way solid manages everything so wonderfully is whenever this function reruns
we discard of everything in in between like what will happen is this memo will get sent into a render effect somewhere that will actually insert it in the DOM
down the line. But essentially you don't want this to rerun unless this changes from true or false. You
don't want you know like stuff inside can change. You can like change data but
can change. You can like change data but you don't want to rerender the whole branch just because count went from one to two unless you do like sometimes like that's what a keyed update is, right?
But but like for the most part and I want to bring this up because every time this runs it throws away the old stuff.
which is the whole problem, right? If if
now this is showing a switch between tab one and tab two, tab one, tab two, right? Pretend it's not showing
right? Pretend it's not showing fallback. We're tab one and tab two, we
fallback. We're tab one and tab two, we have we have a problem because the second we switch to tab two to look at and fetch our data,
we threw away tab one.
And then it occurred to me. I'm like,
what what if we didn't what if we didn't throw away tab one? In in theory, memos are pure computations, right? Do
do you get what I'm saying? Like these
these don't have side effects. Like yes,
it might build a bunch of DOM nodes. It
might um it might do a bunch of you know work inside its own scope but it's it's over here where you're like create render
effect where you read the results of this show you know whatever show component I'm just doing this for whatever
purposes when you get back whatever the children is it's in here on the on the other side of the effect here when you're like children you know whatever they are that you do the actual like
insert work where you're actually like you know parent you know children [clears throat] you know do whatever stuff this is where
all the work actually happens down here.
So the and it occurred to me that if as long as you know
it it probably doesn't matter that we dispose this right away. That was my thinking, right? [clears throat] Because
thinking, right? [clears throat] Because you use an effect like like when you want to make a side effect in the DOM and in the new uh solid um API for
create effect um where you have your dabs and then you have your callback function, you can do the good old React return
cleanup, right? And you clean up your
cleanup, right? And you clean up your stuff right?
you actually get depths back through here the way we're doing it. But what what the the point that I'm trying to make with this is that like um
this is where your side effects happen, right? On mount fact, this is where
right? On mount fact, this is where you're doing most your work. Yes, there
is technically an on cleanup that can run in here, but the what if we don't dispose of this right away,
it's possible that we can have both realities existing at the same time. Earliest version
transitions in solid should go I have like a medium article on this stuff like mediumcom solidjs.
See uh maybe I should just go to my medium page. Let me see if I I've been working
page. Let me see if I I've been working on these problems for long time. Um
where's my stories? my stories. Is this
just like a fake thing where they like Okay, got it. Yeah, thank you. Oh,
actually, I didn't get it. What did it say? Did it tell me that they moved it?
say? Did it tell me that they moved it?
Yeah, they they did move it. Okay.
Uh where was it? Designing SolidJS
substraction. Designing SolidJS.
One of these designing SolidJS suspense.
When did I write this article? Sorry,
just December 3rd, 2019. Okay.
Concurrent mode. Okay.
I showing suspense off here. Okay.
Transitions. How did I do transitions back in 2019?
Use transition. Okay.
Oh, yeah. I did it.
Okay. So,
yeah. You know what? Maybe I updated this article.
This looks like it's already up to date.
Yeah. Suspense list. Okay, never mind. I
I had an earlier version before the 2019 version that apparently works perfectly fine.
Crazy. Um I didn't realize I did it that early. Um
early. Um never mind. Uh which which actually
never mind. Uh which which actually hijack your control flow and you'd pass a transition as an argument into the control flow component so that it could actually just opt into this behavior.
But the gist of what I'm trying to get out of here is that the the the idea is what if instead of disposing what's on the page,
you keep it around and if you detect by the end of this update that there's async involved like that it's a transition essentially.
Well then we don't throw it away and we keep it around and we keep on updating it. But
if we get to the end and there's no async involved and it's not a transition, we just dispose of it then.
And basically we don't fork anything.
We just defer disposal. So we don't create anything new that we didn't need to create. If you're doing an update
to create. If you're doing an update from condition one to condition two, you're definitely going to, you know, tab A to tab B, you're definitely going to create tab B, you know,
but so it's not like we're wasting work.
We're just upping the memory temporarily a little longer. So instead of releasing A right away and then creating B, um we we keep A and B around simultaneously
synchronously till the end of the flush.
Now to be fair, synchronously I don't even know if GC would even have time to operate. Like this might not even be a
operate. Like this might not even be a noticeable overhead. We still need the
noticeable overhead. We still need the benchmarker, but I did benchmark this in the reactivity benchmark and it had almost no it had minimal overhead over R3. It basically brought R3 back up to
R3. It basically brought R3 back up to alien signals levels in terms of performance. Like we we actually didn't
performance. Like we we actually didn't get hit. Now, I'm sure the performance
get hit. Now, I'm sure the performance would degrade a little bit further as I add more solid features on top. Just
kind of how it is, but the reality was this is the fastest version of the reactivity that I've ever worked with.
Um, even with this behavior built in now, they're not testing this behavior in the on the in the reactivity demo.
They don't even have ownership. They
don't even have depth. So like I don't know of the cost of this but the important part is for people not using these features it's you're not paying a
penalty. You're it's the fact that it's
penalty. You're it's the fact that it's transitions everywhere doesn't have a performance overhead. This was the
performance overhead. This was the critical why would you want to keep a around right here
so that a is still working. any of the go. It's so that when you go to tab B
go. It's so that when you go to tab B here and fetch in the background that A is still live. It's the holding behavior that we were talking about. See how this counter is still going. If we just released it, we might be able to do
something smart where we like left it on the screen, but this counter would stop.
This is why I use this demo in every example. This is a live part of the
example. This is a live part of the graph while this async is applied. Now,
not everything's a tab switch, so to speak. This is a more general concept,
speak. This is a more general concept, but um I wanted to which yeah, sorry. Yeah, I can save this. I I
yeah, sorry. Yeah, I can save this. I I
just wanted to kind of show this as an example. Let's see. Can I increase the
example. Let's see. Can I increase the delay here? Let's go plus 380. Yeah,
delay here? Let's go plus 380. Yeah,
let's do this.
See, it's so that these are both existing in time. It's basically a transition, but there's no transition.
There's no forking. It's just the fact that we use a combination of keeping the values in the past and deferring pure disposal i.e. non-sside effectful
disposal i.e. non-sside effectful
disposal that we essentially accomplish transitions as with no extra cost.
Still a few minutes do the research.
Make sure you're absolutely convinced you're happy with our design. Much
better than delay and get it right in the first time. Yeah.
So then you can switch to children without rerunning the call back again.
Well, the thing is if there was no transition involved, you would be switching like do you know what I mean?
Like if you're going from A to B, like you're definitely going to go to B and render something differently. Like
that part never changes. You're going to run A once and you're going to run B once. But
once. But you're going to have it run A and then when you switch to B, you're going to run B again. But what what's cool about about this approach is we can have them
both existing in parallel while that transit transition's happening so to speak.
And we didn't need to fork it. Like
before the problem was I would keep A where it is and then fork it and then test it out on B and then merge it back in. This is way simpler. I in fact I
in. This is way simpler. I in fact I don't have all the features in but solid signals implementation was getting to about 2100 lines of code built and yeah I don't have all the features in the
current one it's about 900 I' I have like half the size of the code complexity with this new approach even though the reactive system with R3 is probably like a tiny bit bigger but yeah
in the sync case the disposal happens after creating the next state of UI traditionally it would be happen before yeah exactly we defer so it's more like if If you view, there's a bunch of cues.
There's a pure Q and then there's like a render effect Q and this user effect Q.
1 2 3. Usually, as you run, we do clean up. So, you hit a node, you go clean it
up. So, you hit a node, you go clean it up, run it again, clean it up, run it again, clean it up, run it again. What
this does, it goes run all the pure Q, collect all the cleanups, then it goes, okay, I know because we've split the effects, we know if we hit all the async at this point, we run all the front halves, we run all the pure cues, then
we're like, okay, now, yeah, this is a transition. Then we go, okay, don't
transition. Then we go, okay, don't dispose of that stuff. Let's keep it around and keep it going until that transition ends. But it it gets the end
transition ends. But it it gets the end in there's like, no, no async. We're not
a transition. Whatever. It just disposes it at that point and then runs the render effects and continues on.
Couldn't the counter be in a global?
Yeah, but it doesn't matter if it's in global state if you're rendering it inside the component because if you
release it, you're releasing the reactivity. like it just it like
reactivity. like it just it like even if I keep a reference to it if I if I if I create a me it's the way solid works is when you because you you want
automatic disposal right if I create a memo inside a memo you know you're going to create a new memo so there's no point keeping this memo around you have a memory leak this is the classic problem with these reactive systems is they
leaky observer problem uh if the if the thing they subscribe to lasts significantly longer than its own life cycle, it stays there connected to that signal in the global scope. So what we
do is we register any children with the parent. So when the parent reruns it
parent. So when the parent reruns it disposes them automatically. There's
ways you can get around that like we use inside four components and control flow like for like control flow because we can release a row at a time but generally speaking the base behavior is
you just release it. Um, so even if you have the signal in global state and you're you release it, but you're still like just pulling there because you haven't inserted it yet, it's going to
not update because you the memo has been released. It's no
longer connected to the signal.
Yes, although I'm going to the technical side.
Uh let me get to the actual examples.
You might you feel sorry what behavior is unwanted.
Sorry I'm not quite following like the the the ownership model of automatic removal is just it's a base thing otherwise you could create tons and tons of memory leaks right like it's
literally well keeping the state around. I mean
the question is do you believe that like do you believe that or you just
mean like keeping the page live while the other page is loading? I mean, it's really, you know, the tabs going away.
So, like it being live is kind of like a like who cares maybe moment, but there's cases where you're doing mutations on screen itself where the whole tab isn't
going away or part of it is going away or like you know like things are changing. you you don't want the UI to
changing. you you don't want the UI to suddenly to freeze up momentarily because you're doing this action necessarily like you know what I mean like animations or other kind of stuff like the idea is to keep both things
live um that's the whole idea with the whole transition thing in the first place right two branches that that's the whole thing the whole time right
it might be notable if there's a reference outside being mutated inside cleaning up yeah if It's not pure, then you have a problem. It's like the React strict mode problem. See, the funniest thing is so many parallels here. React
is like if you just follow the chain inside our create memos. Almost all the things that React realized that it needed are true about the reactivity graph. We're like just one dimension
graph. We're like just one dimension outside. We we've we have our reactivity
outside. We we've we have our reactivity running through the component system.
We're React the component system is the reactivity. But conceptually, it's the
reactivity. But conceptually, it's the same thing, right? They throw they throw their components. They throw their UI.
their components. They throw their UI.
We throw we throw along a data graph that runs through our UI. This lets us run the UI once, which is incredibly powerful in terms of performance and in terms of like just like okay, there's a
thing, but the actual behaviors of the graph are very very similar to what React is in certain thing. We just have a world that lives outside the components which they don't.
Yeah. Well, no. Effects are fine.
Effects are fine. The effect cleanup is going to happen in order. It's only the pure side that matters. Effects are
fine. It's only inside memos.
Effects will run one, two, one, two, one, two, one, one, one, two. It's just
on the it's we defer pure disposal because the effect the part that I haven't showed is the secret part is the effect can read the render effects read
the old values. See normal effects read the updated value and they throw and they get caught up and they and they they don't update. Render effects
can read the old values. So they might run because like what happens if you in the same like they might need to run um
with like while something's holding and something's updating like that counter they might need to run at the same time where you have both pending value and non-pending value. So render effects
non-pending value. So render effects actually they don't they kind of tear in a different sense the word tearing. Um, so it's the UI
that does like the actual like holding of values in the past in a sense even though it's like represented as in the effect.
I believe the problem I believe there are ways to keep the counter alive but still cannot get problem with return clean up. There
there's no problem with return cleanup.
The return cleanup here is fine. This is
perfectly fine because it's in an effect. You do the side effect. I'm
effect. You do the side effect. I'm
saying that if someone inside here was doing on cleanup like inside this memo if you keep it around it's possible like this won't run
right away. This is fine. Your effects
right away. This is fine. Your effects
you most developers put their cleanups in effects or they should be or on mount or like like And even if your cleanup's here and it's
just temporary delayed, it's probably not a huge deal. Do you know what I mean? Like
mean? Like usually when it's a resource subscription thing, it's happening here, not here.
Yeah. Well, that's what I'm getting at.
Memos are pure, right? Like that's what the assumption is of this system.
You know, that memos are pure, which they've kind of been this whole time, right? It's the
reason Solid One has create computed. I
don't didn't want people writing to state inside create memo. We haven't
enforced as much as we could, but we literally gave people a primitive for it so that they wouldn't use memos like that.
Of course, they can use use effect as or create effect as well, but my my point is this deferred disposal doesn't actually cause
any extra work to happen. You're not
forking. It just it is a it just chang this change of timing on the pure part essentially allows for us to avoid
forking and my guess is that spelt kind of does the same thing because their control flow is special. They probably
associate with the blocks and defer disposal until the async resolved on the blocks. This is happening at purely a
blocks. This is happening at purely a reactive level. That was my idea. I was
reactive level. That was my idea. I was
like what if I could do this literally in the reactive system? Now, it's tricky because you have to cue them separately because if you cue if you take the pending nodes and you just stick them in
the normal queue and the you don't always want to run the pending node.
Like I had this problem where the thing overran like I had a test in in the repo where it was like it ran the inside one one time too many because on that last time you don't want to you want to
dispose and not run the pending ones.
But [snorts] I realized if I mark disposal and isolated queuing, so you have a normal queue and the to be disposed Q, we could
basically manage this. Um, and then I just went through a bunch of different scenarios to think about how the merging would work. And
would work. And you know what?
I I my my my takeaway was that since this would work I would be able to mark our existing nodes and just give them pending values pending dispose and pending dispo and pending like children
dispose and in a sense it if anyone's seen solid's current implementation we have something called tv value that's very similar like where we just hold the transition this was like just like I realized that this approach is basically
solid 1.0's 0's transition algorithm just done cleaner around disposal so that it's actually guaranteed like it's actually systematic instead of like 1.0
transitions have bugs in them like if you get complicated where this approach systematically I can make sense of and explain doesn't have the same kind of level of bugs. It's not like like we if
you look at the disposal code in 1.0 it's crazy like you're like if you're a transition didn't do this and didn't do this. This logic is like much more clear
this. This logic is like much more clear that you just have a queue of the dispose with marked dispose nodes.
Anyway, um because I want to get to the examples here in a second. Um,
yeah, I think I ended up just codifying this stuff more over time, but um, yeah, you know what? I think this
article is actually newer than the Yeah.
Yeah.
Yeah. This article, I'm not going to get into too much. just talking about the fact that actions are still required. I
think that's beyond the scope cuz we're not covering actions today. I do want to pull up one more spelt playground though to play with cuz this is this this is the one where I realized that I'm like I don't want spelt behavior. So, let's
let's talk about it. The idea is what if you have two states? Like I couldn't figure out for the life of me how spelt entangled the transition, but it's so
obvious in hindsight because I was like it I I came up with three buttons and two states. one state triggers derived
two states. one state triggers derived and the other state has nothing to do with it and going to update right away.
So this like non-async value, if I just click it like a bunch of times, it just updates immediately. This async value,
updates immediately. This async value, if I click it, there's like a delay to when it updates, right? And if I do it together, it's a
right? And if I do it together, it's a delay to when they both update.
But what's interesting about this example is like when I press together and then press non async there's like an interesting jump where it goes from
like okay now they're 2020 now I hit them together okay which should be 2121 right but then I'm going to hit this one and it's what it's going to do is it's going to show 22
before the other one updates which is weird because in a sense you you you would think these were entangled, but spelt doesn't have the same idea of entanglement. It's weird
that you can like see the update from the together button press before together resolves. Um and
together resolves. Um and and and it occurred to me that it was so simple. They they weren't using the
simple. They they weren't using the reactive graph to make their decision completely. They were literally looking
completely. They were literally looking at the synchronous timing. Like I was like, what how do I know these go together? It's literally, sorry I keep
together? It's literally, sorry I keep on saying literally, but it's just the fact that in within the same event handler, same synchronous frame, that gets part of the transition, but it's not a transition because if I click this
10 times, you're going to see 10 updates on the screen. They don't entangle the same thing. You probably have to handle
same thing. You probably have to handle cancellation yourself, you know, which is something that you don't in React or Solid. So, I was like, okay,
Solid. So, I was like, okay, this is more complicated. Um, but it should be doable. So, let's let's that that that kind of realization here
in terms of of like these these behaviors led me to kind of the the solution space that we internally model something very similar
to a transition but externally you don't have the wrapper for it. So how does this look like? Okay, I I I know I've been going kind of deep here but let's
actually look at some actual examples now. Um,
now. Um, and I'm actually going to do them in the opposite order. I want to start with um
opposite order. I want to start with um the async holding example. Okay. And I'm
I'm going to clear out a few of these actually. Perfect. I have the felt one
actually. Perfect. I have the felt one right here.
Sorry. Actually, that's funny. I have
the salt one. I want my my version.
Leave. I want
this version.
Okay.
Now, this is the same example. I've done it with with um just handwritten no JSX because that's I haven't implemented the JSX, but I want you to notice that these
are kind of grouped together like they're part of the same effect like
like essentially the one the one the the multi was it the value and the and the result are part of the same effect. So
like there's this one actually doesn't that aspect doesn't matter. Um in the next example it'll matter. So actually
forget about that. But essentially if you look at the difference between these examples they're very similar. They have
derived a weight and we have create async but it's the same idea. You call
multiply on the thing. Every time you click the button you just go n plus one.
Okay. It is not um it's not like any kind of difference in terms of concept. It's just there's no transition and you go is pending N,
right? You can just ask that about the
right? You can just ask that about the value. So when I click this, it delays
value. So when I click this, it delays and you see the dot dot dot and then it shows the value like you expect. Even
though these all have random timers, they all get tied together. And what's
more interesting is if I click this four times, it only shows it when it's done.
Click it twice.
There's no jitter state. This is like React transitions. We're actually we're
React transitions. We're actually we're actually stabilizing them. Whereas in
this example, I click it a bunch of times, it does that. Right? So, this was kind of like
that. Right? So, this was kind of like the first behavior consideration, right?
So, this would be like the the base default, right? There's it's hard to see
default, right? There's it's hard to see this without seeing the JSX and I think it'll be easier when people see it. But
what this example is showing is with no transitions and just introducing async, we get the holding behavior and the entanglement automatically and the obviously the ability to ask the
question of is pending.
Deferred disposal allows not to kill some nodes. Yeah, it allows us to keep
some nodes. Yeah, it allows us to keep both branches live at the same time without forking them. It's kind of like the reverse.
And once you realize that you're working towards a single feature anyways, that transitions don't really roll back um then this is actually a fair assumption. You can basically do zero
assumption. You can basically do zero extra work, just defer some of the work and it actually just works. Um,
so what's cool about this isn't this is this basic example doesn't show very much, but it just gives it starts getting you an idea of like how the
behavior should be, right?
Next example.
Actually, I should probably share this example in the chat just in case anyone wants to check it out.
next example that I want to show here and kind of explain it. As I said, people feel free to ask questions as we go. But this parallel async example um
go. But this parallel async example um which I also made in spelt this one is important because what you're seeing here is there's a single render effect
that literally has all the stuff. It has
the ID that we're going to increment that we're going to use to fetch our data and it's going to have our data.
It's going to have a count, which is just literally a counter that's going off in the background. And then it's going to show pending like what what the ID we are fetching it looks like. And
then I have a second render effect which sets the div opacities while it's pending. Um, essentially again the u the
pending. Um, essentially again the u the UI itself is just setting ID, right?
We're literally just going ID plus one.
So there's no transition, no nothing.
This is just the base behavior. What
you're going to see here is as I click this, it's, you know, doing dot dot dot loading one, loading two. And if I click it twice, 3, four, five, six, and then
jump straight to six being loaded. So,
we have the ability to inspect what will happen before it happens as as well. So,
we have both capabilities of showing the loading while it's happening. And the
reason I show this example is because I can kick it four times without and it only drops in when it's done. And also I can have these running in parallel without impacting each other. So even
though these are entangling because the graph doesn't overlap there's actually no um like the these are independent updates. Now like obviously if I force
updates. Now like obviously if I force the graph to overlap like right now this is rendering two components data 1 data 2 right if I did the old let's make local state global state like I'm
pretending it's like a component like just hoist it up then obviously these are going to be the same thing right and then they're going to tie together right that doesn't take a genius to understand that
you know what I mean like that when I update these they're the same thing and they get entangled but it means that purely non-entangled async has the potential to run
independently, which I think is important in this model because if you can't, you know, if you're not going to be setting the transitions, you want it to be possible for people to to have multiple updates in flight on this on
the same thing, right? So like the what's spelt doesn't
right? So like the what's spelt doesn't have entanglement. So obviously parallel
have entanglement. So obviously parallel works but it does do 3 4 5 6
4 5 6 7 8 like um so yeah it's interesting. Yeah I I guess I can't do the global state example as easily.
Um, I'd have to pass it in as a prop.
Um, it doesn't really matter. I I I I I don't I I'm pretty sure salt will behave the same other than it doesn't like entangle the stuff. But generally
speaking, um, this is this was like very helpful to kind of understand this. And and this was actually the example that I was working with with Tener that he got excited with because he realized something when he looked at this. He was
like, "There's no transition here, right? It's just you you you build
right? It's just you you you build something and if you forget to put a loading indicator on it, you know,
it's funny because I actually have the pending data in here, but so there is a loading indicator, but like if you forget this stuff, it still works in a reasonable way. you just you know you
reasonable way. you just you know you it's almost like the language that's describing the the thing you're like okay wait it's working but it's it's delayed how do I show you know a loading
indicator ask for is pending you know like it's there's something very natural about just like the basic the base version the way
it works and then just being able to ask this information now obviously if if I do a console log here for this to perk.
You You know what you're going to get?
Um when I click on this, you're going to see zero even though one, right? Three. Now it's I click it
one, right? Three. Now it's I click it three times, you throw it one three times. Now of course if I do pending
times. Now of course if I do pending data ID then we will get all the numbers 1 2 3 4 right but
though it's starting at zero which is interesting so maybe I don't have that working quite perfectly yet. Oh, right.
It's because right now pending is deferred a micro task. I'm going to have to I'm going to have to look at my implementation again on that. Okay,
that's fine. It's because I I needed to to not entangle, but I I probably have a different solution to that. But anyways,
I I don't know if it's obvious to people how dope this is, so to speak. I because
the I mean it's really obvious like you got to pretend this is JSX and stuff but it's really obvious when you're literally just using the reactivity system and it just works like this like there's no transition there's like all
you're asking is is pending or get me the pending value. Otherwise you just use the graph like normal and it's and it's and it's just like it just works
like it I I I don't know. I I think this is really cool that there's no transition wrapper. There's no you like there's no
wrapper. There's no you like there's no uh like framework aspect here like this is your compile code you know some people like show me the compiled code this is your compile code there's no
compiler here this is this is literally I just made uh a component and then um just you know appended some div elements
together you know it's it's it's literally just the reactive system doing this by its very nature being async first.
Yeah, I mean in terms of splitting out the uh the depth is the same because it has the difference is this runs during the pure phase and this runs on the not pure phase which is important um because
you want to be able to know about your async dependencies and figure out how to handle stuff. Now you could compile it
handle stuff. Now you could compile it in you could but then you have other issues. Um you could define another
issues. Um you could define another block scope to tie async to that's not based on the reactivity react uh you know but then you need a way of identifying that like spelt does with
its compiler but yeah this is th those are the trade-offs unfortunately setting if once you have that ability
and it's hard to explain like or showcase like I think this is something where we like you you you ask someone to create an app
the way they think they would create it and the with using the tools they to do today and then I think Rick actually did this on learn with JSON and then you just delete like all the the problem is
this deletes so much of the code like to to create this exact behavior in in in you know a framework without the async
features requires considerably more like it's way way more complicated um compared to just like this I think The select example, Ryan Florence is going
to be it. Yeah. No, no suspense, no transitions. Yeah, exactly. I mean,
transitions. Yeah, exactly. I mean,
suspense is still important because right now I'm faking suspense.
Uh, sort of. I mean, you don't need suspense, but this like loading state I'm doing right now by literally how am I where's the dot dot loading the
initial one? I'm I'm I'm setting the
initial one? I'm I'm I'm setting the text content when I first build it.
Ideally, what you'd do is you'd have suspense around this and it would just handle the loading initially and then after that like you will never go back to suspense again because like the way
this works it just suspense will never trigger after the first go. It's just
it's it's because you're automatically in like that transition zone. But yes,
you suspense is not necessary arguably unless you're like streaming. Um I think it's still probably good thing but you can there's a difference. I'm I'm
thinking renaming the suspense component to loading component. We'll have error component and loading component very kind of clear thing like suspense is just for that initial load like when you don't have the data yet and then
after that you just ask the question you're like okay now that I've loaded your afordances are going to be like is is it pending that's basically the question you just put that everywhere.
So like you you view suspense as like uh how you lay out your page like where your loading indicators are like for initial load and then once you're doing stuff and you're doing all the small affordance work, you just use this pending everywhere and then you're
you're good to go. You know what I mean?
You don't have to worry about suspense.
It's never going to jump back on you. A
lot of people were worried about suspense and they never they never use solids features like async features cuz they're like, "Oh, suspense just keeps on ripping out under me." And then I'm like, "Oh, then you use a transition."
And they're like, "What's a transition?"
And then you're like, "Or you can use latest." And they're like, "Okay, screw.
latest." And they're like, "Okay, screw.
I'll just use latest, you know, which is kind of like almost like this pending value." And I was like, "Yeah, but then
value." And I was like, "Yeah, but then now you're like not leveraging any of this stuff. It just, you know, like
this stuff. It just, you know, like now they don't have to worry about this with this API and position.
Suspense will never yank out of you."
And like stuff will work. And if you want to show a loading indicator, you just show your loading indic or your pending indicator. You show a pending
pending indicator. You show a pending indicator.
Yes, you that's the thing you switching back you lose it. And to be fair, solid 1.0, solid 2.0, every version of transitions
that I've ever done, switching back loses it. I react switching back loses
loses it. I react switching back loses it too. Except it's a virtual DOM.
it too. Except it's a virtual DOM.
Switching back has never been like there's no cancellation. It's eventual
future. So if that eventual future switches back to if you go one place and then come back again you you you've moved through time. You basically what would happen if no transition was involved and you went to one place and
you went back to the same place you would recreate it. This is no different.
Yes. Not for going back to the old one.
Yeah, there's no like suspense doesn't go away. Like we still have suspense.
go away. Like we still have suspense.
Suspense lets us render stuff offcreen.
Like the thing with suspense is you um and this is like full tab swapping. Suspense when you're tab
swapping. Suspense when you're tab swapping is not going to make a difference. The thing what suspense lets
difference. The thing what suspense lets you do is like when it rips it out of the screen, it's still live and it comes back and it comes back again like and you don't throw it away in the middle.
But that's that's not this use case. We
never go back to suspense. So you never have that ripping case. Do you know what I mean? Like this is like going to a
I mean? Like this is like going to a whole different page even under suspense that would Yeah.
I was I do think the be very cool and better than previous solid 2.0 model.
Okay.
F doesn't do this. It never disposes the main branch. Yeah.
main branch. Yeah.
Yes. Yes. Yes. Yes. I mean to be fair in the JSX you're not writing that event dependency array. Um but yes. Um and it
dependency array. Um but yes. Um and it doesn't have to be a dependency array.
It could be dependency object. It
literally does it's it's I array is just the easiest way of accessing it through.
Um it's it's literally this is a function. It could be a single signal.
function. It could be a single signal.
It's just an expression. it the using array was just like the way like compiler actually uses an object I think and gives each one a unique key um and
then yeah like our JSX compiler but it doesn't it it's it's more it's less like we're using it like an array because we pass the value through but it's more
like a views uh watch what watch views watch watchers Watch question. New question, old
Watch question. New question, old question. This is the API port
question. This is the API port essentially. I said I never got anything
essentially. I said I never got anything from Vue. Here we go. Got it from Vue.
from Vue. Here we go. Got it from Vue.
There we go.
Honestly, it was just pure coincidence.
But see, you watch X as I sum this. Like
it is literally like you can make it making an array makes sense. The only
difference is we don't put we don't do this on thing where we put the array on the outside. I This drives me nuts. I I
the outside. I This drives me nuts. I I
know it can make it shorter sometimes when you have straight signals and stuff, but like honestly then you get into like more complicated cases. You
might as well just make it always accept a function because when you when you accept an array, you actually have to iterate over the array internally. It's
like slower. It's it's just there's just no point. But generally, it is just
no point. But generally, it is just watch which means that we also have to support the deep keyword um for stores which um has a lot of optimizations baked in. I spent a whole stream talking
baked in. I spent a whole stream talking about how I was working on that. Anyway,
yes, it's basically like views watch. It
doesn't have to be an array, but yeah, it's very similar to spelt's new model except it provides the guarantees thing.
I've been actually been showing spelt examples inside the solid one like like this like this is the the same example in spelt using state eager here I had to do something because I didn't have
pending I had to do eager not equal year but I mean it's it's fine uh but like the only difference is spelt the biggest difference is the way this this goes one
16 17 like I click it a bunch of times and it goes like this I mean this is such a minor thing perhaps but the the Other difference is um so like in the solid version when I do this what you're going to see is it's just going to load
in once. It has the same parallelism
in once. It has the same parallelism where I can do this one and this one at the same time but it's just going to when it's finished like eager is this thing at the end here that as I click it
you'll see count up 13 14 56 17 but it is very similar to self model except it has the same integment link guarantees that you find in react that
built before um and it doesn't require a compiler. This is as you can see I'm
compiler. This is as you can see I'm using a DOM here. There's no compiler involved. This is purely built into the
involved. This is purely built into the reactive system.
Yeah, you don't write v 0 v1 usually.
Usually people use objects and uh like a lot of time people do independent effects. Um to be fair I I was using v 0
effects. Um to be fair I I was using v 0 v1. Do you know what most people do in
v1. Do you know what most people do in these cases?
They just destructure and then they just do this.
Let's be what is this one? this one
count and then data and it's way more easy to read it. data ID not equal ending data
read it. data ID not equal ending data I did I be where I have a B somewhere still
anyway I sorry I to be fair this This is more of like what the yeah
this is mostly compiler output. I'm
literally writing vanilla code. And as I said, if people did stuff, usually you they would, you know, make an array
and destruct the array or make an object and do pd data, you know, you know what I mean? Like there's there's lots of
I mean? Like there's there's lots of ways to to do this. Don't don't get me wrong. I'm not like absolutely stoked
wrong. I'm not like absolutely stoked about the result of this, but I'm kind, you know.
Yeah. I Yeah. No, it's true. Um, this is one of those things where like [clears throat] it it and I made an on helper
so that you could put it on anyone, but it doesn't make sense on the pure side to have on as much. So like yeah, this
is a bit of full circle going here.
Yeah, I mean we're catching up. We could
also do a dependency object. Yeah. Yeah.
Or dstructuring. Yeah.
Well, in the ren Yeah. The way that effects work. Um. Yes. to basically uh
effects work. Um. Yes. to basically uh render effects are interesting because how am I updating the count right now
while it's while it's while it's loading like look count is right here I'm running this I'm clearly running this effect you know like console.log log
hey. If I put hey in here, what you're going to see is hey running over and over again. And
then when I click here, hey is still running.
It's that because we don't commit the values. We know that when we're not
values. We know that when we're not currently under the active transition so to speak that we can show the stale values for things that are think or that are like when we're under an active like
under the a flush that's actually been working on the async like which is usually really easy to detect after the fact because usually you're coming back in because some async resolved which means you can immediately be like oh this async resolved we're back in
transition mode you know before you get here right in the height graph. um
because you know then you can always sh use the latest values and most time when you're doing reactivity you can do the latest values. It's only when you
latest values. It's only when you encounter something that's part of a transition and you're not part of that transition that you show the old values.
Um when you're in a render effect every other place you always enter inside the inside the graph is always the latest values. So this is this is basically
values. So this is this is basically like the one exception is is to every rule is the render effect. So there is like a framework aspect, not framework but like a render UI aspect part that
make render effects special. But other
effects um like user effects don't get brought into this. They don't affect uh they don't affect um transitions. They don't affect suspense.
transitions. They don't affect suspense.
They don't really care. They're they're
just literally when their depths are stable, they run. They won't trigger suspense. They don't care about that.
suspense. They don't care about that.
They don't trigger error boundaries.
normal user effects are kind of independent of the rendering. So, um,
which I've shown in other examples is very very powerful thing because then you can like even though they won't run like they'll be held when they're off screen like if they haven't been
rendered on um they they won't be the the thing responsible for stopping things from being rendered to the screen.
We use objects actually because the it was better than using the array. So we
use objects with with uh with things and I might be destructuring. Yeah, I'm not sure.
Sorry. It's just funny. You can't like throw a rock two inches.
Um, yeah, they're they're relatively easy. I think they'll be easier in 2.0,
easy. I think they'll be easier in 2.0, too, because we were more aligning with HTML standards in terms of most of our conventions. But yeah, I mean,
conventions. But yeah, I mean, straightforward. I have my own opinion
straightforward. I have my own opinion on web components. You should watch just Google me and web components and you'll see Does this mean no generators or resume?
No. Uh for basic you you do need something for actions, right? What's cool about this is you can see is in any case in which you are updating like like these
pending things like where you're just like do doing the [clears throat] stuff for on the fetch side not on the post side you don't need optimistic you don't need transitions you don't need any of
these other mechanisms like you're literally just going hey am I waiting or not like that's the question you're asking when you're doing async there's the first question you're you're there's only two questions I actually covered in
my talk it's like can I render this and is it out of That's what like can I render this is suspense essentially or the loading component and is it out of date is the
pending like or is pending like that that's basically that part. Now once you get to a place
that part. Now once you get to a place where you actually have to the problem is mutations don't have a dependency graph like they don't have anything to tie them. So, if you have multiple
tie them. So, if you have multiple things in flight, you need a way of like deciding that they're complete and you do want to hook the updates together.
I've talked about this before in past stream because you if you have multiple things in flight, you want them to entangle when they affect the same state, but you don't know. Optimistic
can help guide you to tell you like if they share optimistic state, then they probably they're entangled as well. But
it means we still need after the await a way of connecting the like data refresh or like the set state after mutation mechanism back to the thing. So this
problem does not go away. It's possible
that instead of having a transition, we have an action helper similar way. But
the difference with the action helpers is very very deliberate like when you're performing an action we we already have APIs like this in the router. So we
wrappers where you can like define them globally. I haven't completely landed on
globally. I haven't completely landed on it but the core part is with this sort of API design no one should be thinking I should go into my design system and
make select action design thing you know like that we were on that path right um but now since everything's a transition you don't need to go update it like the
router just works this way we don't need the router to go do wrap stuff in transitions your input components don't need to go like you you don't need to go through your whole ecosystem and be like
okay component authors cobalt guys come here go update all your stuff that use transitions like the the the end user will go like okay I have an action I
want to do with optimistic usually it's those two things you'll be like because you could technically if you don't have optimistic state you're probably not going to notice the entanglement problem
so most people who come in and do something naive will just in a click handler do the thing. And yes, it's if if they click it a bunch of times, it's possible the timing happens that like
the transition doesn't start until um it comes back from the action and they might miss each other or they might overlap and entangle, but they'll generally be okay. The second that you
go, okay, I need an action is when you go, I need optimistic state. At that
point, there's a real start and end. And
and in that case, you do need a way of removing the optimistic state, which means you do need a wrapper and you need to do it. But what I'm saying is people will get 80% of the way even further
before they ever realize that they need the action API. And and I think I think that's that's okay. You know what I mean?
That does that make sense? Like because
if you don't use an action, it will just view the stuff before the action and after the action as separate transitions, which is fine. If if if the
graph overlaps at some point, it will entangle them, but it's I I I think it's only the optimistic state that really gets us in trouble. So
that's that's when you know when you go when you get to that point where you're like I need to check the box ahead or when because I you know or I I mean even
checking the box ahead is not that bad because it's well no it is because you can tell you can tell the difference between an action and that because when
you do um something on the get side it's usually triggered from setting state right whereas an action is not triggered from setting state. An action is
triggered from like doing something async like up first like doing a post.
So like if you if you had something where you want to save a to-do whenever you checked it done, right?
There's two there's two things you do there, right? You check the to-do and
there, right? You check the to-do and then you if you want it to be optimistic and then you go save to the server, right? You can tell right away that
right? You can tell right away that that's an action because the the fact that there's a separate action, the actual saving it to the server. If all
you had to do is check the to-do and there's no saving to the server, then you don't need an action because you could just use the eager checkbox or something. But then, you know, if
something. But then, you know, if there's some downstream async that you wanted to do, right? And then the thing is I haven't actually showed the next example which is um the Ryan Florence
demo because solid doesn't have controlled inputs by default. Um
this demo lets the inputs get ahead. Like if you check a box, we don't reset it on you.
So you don't need to worry about um setting it to eager. You know, spelled the same way. So, if I go to Florida here, it's going to change to Florida before the UI gets consistent. See,
look, Florida, but now it's Miami, Florida. So, it didn't change the UI
Florida. So, it didn't change the UI until it could show Houston, Texas. And
while it's waiting, it's graying this out. But this shows California briefly
out. But this shows California briefly while this is graying out. So, generally
speaking, you want your inputs to tear.
It's because that's the best affordance for them. Having So, having inputs tear
for them. Having So, having inputs tear by default is kind of cool. And this
example is exactly the same as what we've been seeing, right? I'm going to close these ones down for a moment. Um,
like what I like now is it's like the spelt example. We don't have two-way binding,
example. We don't have two-way binding, but we have a select component now where I've generalized it and you know state states set state on
change. See, I'm just literally passing
change. See, I'm just literally passing the setter. Now, if this was data
the setter. Now, if this was data binding, I could go val I could do this in the same property, but who cares? And
then create async get the states then the state zero is the state and again yeah
just beautiful kind of logic graph here of just explaining how whenever the state changes fetch the cities for that state and then se and then whenever the
cities changes the selected city becomes the first city in the list right like it just chains down and you know if you want to add a third one you just add a third one you know that's why I did the country example But what's cool again is
in in here and then I showed the selection with a render. In fact, I show state in this.
render. In fact, I show state in this.
Let's put this example in the chat.
Yeah. Yeah. Yes. Yeah. Sorry. Uh this is true. I've been I've been I' Yeah. I've
true. I've been I've been I' Yeah. I've
been calling it latest pending eager. I
I I just Yeah.
Well, um the the the way it works essentially is create async turns async into a promise. Um and which means that get state here is a promise of string,
right? From a string, but states here,
right? From a string, but states here, oh right, Typescript isn't states here is just going to be um a string array.
It's just literally knows that it's a function of string array. Like I don't have the types in this playground.
Sorry, it's just this this called a TS file, but it's not actually. It's the JS output from the build. Um, but
essentially, it's non-nullable the way it works. So, what it happens is when it
it works. So, what it happens is when it hits a value like this that it can't read, it throws, but it only throws with inside this expression. And then this one goes, okay, well, I'm async and I can't resolve, so I throw. So, when I
read this, this throws again to this expression. This throws to this
expression. This throws to this expression. And then in the UI, we don't
expression. And then in the UI, we don't throw away the whole thing. We just
again throw within the scope of where we read it each place like here or here or you know um here we we we we only throw
we it doesn't cause us to rerender again. We only throw within the specific
again. We only throw within the specific effect or the specific reactive context.
Um, but what's cool about this is it means that the graph is kind of blocking um as data resolves, but the rendering
is not blocking. Um, and it makes it colorless because your component here has no clue that value or like this component has no clue really if value or options or any of these things are
async. But you know, you just have to
async. But you know, you just have to make a decision based on like like from an affordance standpoint, it's like, okay, well, if options are loading pending, then I'm going to show loading.
This is how I fake suspense here because um it would throw here and then we wouldn't get like there's a is pending has a like a default value that's used very rarely.
Um but so this means that initially when it throws it returns true. Um which is how when I refresh this page, it's actually loading. um not important
actually loading. um not important detail but my my my point is that like um what is my point? My point is that
essentially you consume the async at the edges of the system where it comes into the system and make them part of the reactivity graph so that your components don't need to worry about that detail.
They just basically are only asking a couple questions which is show me the value.
Is this the most up-to-date version of that value? That's that's basically the
that value? That's that's basically the gist of it.
So yeah, I mean what's some people saw this example and they're like is it weird that it waits though? The whole
point is the consistency. You don't want someone to see Miami, New York, right?
If I go here or sorry, let me Yeah. See,
if I'm here, Miami, Florida, you don't want it to be when I switch to New York to be Miami, New York here, which is what the D follow a lot of frameworks
would do um without a transition type mechanism. But this just works as I
mechanism. But this just works as I said, built into reactivity. And what's
so cool about this example is the select component literally just there's no suspense or anything. It's just like basically if this true wasn't here, it's not its responsibility. If I was
creating this for real, I would not have this true here. And when it originally loaded, select would be like I can't render and it would throw and suspense above it would trigger and show the
loading state. But after that point,
loading state. But after that point, it's capable of showing if it's ever out of date by the simple is pending wrapper here inside the select. And what's cool is because it's asking about its own
options. Doesn't care about anything
options. Doesn't care about anything else. You could also be like um
else. You could also be like um sorry. I mean, you could also listen to
sorry. I mean, you could also listen to other things, maybe value, I don't know.
But because it only cares about its own options, um it's generalized, right?
like this first select list has that same as pending check but because its options are never only pending at the beginning here never afterwards um
it doesn't trigger it only triggers this select to be loading which is one of the beautiful things about the modularity of this approach anyways if it throws how does it know when to retry does it await the signal that
through yes uh it's it's essentially not the the not ready error. Um
I mean the way I'm doing it right now is it throws it actually has a link to the node um or the promise if you want. So
we just what happens is the create async um primitive when it resolves
it um it actually sets the signal again which propagates the change down the map. So it's like initially it's like
map. So it's like initially it's like I'm not ready. So anyone who reads it throws and then when it finishes it sets a value, clears the error, propagates that through the graph.
creating results.
If it gets a different if sync gets a different promise than Yeah. I mean, I'm trying to
Yeah. I mean, I'm trying to Yes, actually there I actually had to fix this bug solid uh the the the the lazy version because the way it pulled
didn't matter. But in this version, I
didn't matter. But in this version, I actually had to look and see if if I got a new not ready error. I had to compare them and make sure um because when I didn't it actually didn't update properly um because it was already set
at that status and it wouldn't propagate properly. So I I know that's more of a
properly. So I I know that's more of a mechanical question but yeah would love to see Miami New York only because of deferred disposal
as a city depends on state so in theory it shouldn't happen. That would see me at Miami New York. No no no because the the the there's no deferred disposal on this because we're not releasing
anything. There's no condition here. The
anything. There's no condition here. The
the thing is the reality is when we switch to Florida here, we have Florida and unknown. So show
continuing to show Miami here briefly.
Um in that you know like in here is us holding the value in the past
but like or and like here Florida here like showing Texas even though we changed the Florida still showing the value in the past. My hope is and I don't know if we get this right away is
if this behavior is intuitive enough I'm never going to have to explain transition to anyone ever. Do you know what I mean? like it's it's just like this is async
is it pending or is it not? I I think that's the the kind of takeaway.
Yeah. Yeah. No, this is this is the gist of it. I'm just going to go into this
of it. I'm just going to go into this week in JavaScript, I think, now. Um if
there's not any more questions.
Um, a lot of cool stuff happened the last few weeks, but I I'm going to continue my work on this. Get the uh get the um JSX compilation working. It's
the same output of the JSX. I just need to I need to finish the rest of the implementation of this features in the signals library to actually have it uh you know be able to slot right into solid. Right now I've only I like I
solid. Right now I've only I like I haven't even done four component internals yet. So it's I literally just
internals yet. So it's I literally just have the async and sort of transitionesque model working. It's just
lovely because there's no transitions, you know. It it is a transition. It has
you know. It it is a transition. It has
the same behavior, but there's no transitions.
Yeah. Yeah. I mean, as a as a concept, I think so. I I think spelt is very much
think so. I I think spelt is very much on to something here. I just think that the the behavior just needs like a little bit of tweaking. It's hard. I
spent so much time recreating React's behavior in a more granular way or parallel way, but I I spent enough time to understand the reason for it.
And I think that uh I think React nailed that part. I think it's just we we just
that part. I think it's just we we just need to kind of blend both aspects together. I think it's really really
together. I think it's really really compelling and the fact that I said this just the reactive system is really cool.
Okay.
Very promising. Yeah. Yeah. I I mean, as I said, Tanner saw this and he was like, "Man, this is a freaking game changer."
He wanted to sell it before I was even finished providing it. He was already like telling people and I was like, "How do people do that?" Like, "How do they go on Twitter and be like, I got something really cool and it's like 500
likes.
I I I I literally post the thing, you know, showing it and it's like, oh yeah, 100 maybe. You know, no one gets the
100 maybe. You know, no one gets the mechanical stuff. I think when I make
mechanical stuff. I think when I make the actual demo, it'll be different, but like with the JSX and actually working and people can play with it fully working, it'll be different, but it's just like it's just [snorts] like there's a there's a technical
accomplishment here as well. Um, and
it's important to talk about this and the trade-offs and implications of this kind of designs up front so people understand like for example the set state thing pretty upsetting for some people but
what's the difference if you're going to like call flush anyways you know what I mean or this upupdate you know whatever you know thinking of all the recent Yeah, ads.
Yeah. Um, I find this API to cover all my use cases, give me flex to doing what I want with UI while I'm reading the default experience. See how Tanner
default experience. See how Tanner already saw this and I can't waitition of RC's.
Well, I mean their implementation RC's is actually RSC's um different architectures based on my article like the I covered that in my last stream. Tanner was actually
snuck in there before we did the microrend things and we actually talked about it a little bit. Um no one noticed which is the hilarious part about it.
Like all the stuff went on Twitter blew up but we actually I had Tanner on the previous week actually talking about it.
Um, and you know, cuz he got really excited again. I wrote up something. I'm
excited again. I wrote up something. I'm
like, "This is how you can do it." And
Tanner's like, "Yes, this is it. This is
it." And I was like, "Okay." Um,
his but he they're not making the generic serializer underneath the hood.
I was hoping they'd use servall. So,
realistically, it's not until Solid Kiss the feature that you're going to see these things together.
Transitions were the hardest thing when you started using solid. I think there's one gotcha here. It's the same issue that I see on solid router all the time.
You know that issue. Someone's going to go in and the router is going to work like this by default because wall solid works like this and they're going to put some async on the page and they're going to be like why when I navigate to the next page
does it hold on the current page and it doesn't show it till it's finished loading and and and you're going to be like look you can use is pending and or like is routing or
something. we'll have something
something. we'll have something equivalent from the router but that confusion is still going to exist or actually the other solutions you can nest a suspense boundary and that will isolate it that the the nested suspense I'm keeping the same transition behavior
so nested suspense boundaries will bail out like if you if you have like some global transitioning kind thing happening if you click to go to the next tab but you the next tab has a suspense
boundary we will opt out of the transition and just show that loading state and so which means it will navigate instantly there are ways to opt out in the same we opt out of
transitions today. So I think that like
transitions today. So I think that like um I think while you won't have to think about oh is this a transition? How do I do this? When do I transition? All that
do this? When do I transition? All that
like that whole category goes away.
There will be a new baseline here where it's like oh that's how async works.
Do do you know what I mean? Like it is a it is a shift. It's just like you're not going to be like when to transition is not going to be a question.
Yeah. Maybe post links and not play. No
links to playgrounds. Yeah.
Maybe you had to go through creating every chance to get this right. Yeah.
No, I mean it's it's it's true. I mean I I the to be fair earlier versions of solid were like that already but this version that I did was like the most
extensive version and it's a lot of work. Took me several months.
work. Took me several months.
Solid needs better marketers. I agree.
We'll get there. Harder always says like Solid hasn't been released yet, which is kind of a smack in the face, but I I get what he means.
It's it's kind of the other way around.
Transitions are global. So, it's more of like transitions actually, this is the hardest part about it, modeling it. They actually
it's like the tree falls in the woods kind of problem with same problem with suspense. It's yeah you have to you you
suspense. It's yeah you have to you you do propagate down the graph but it's the effects that actually the render effects that actually decide what the behavior
is. So whenever a new suspense boundary
is. So whenever a new suspense boundary is created it's it takes priority and it will and when you get to the effect and
it goes hey I'm not good. I may async, you know, or throw or whatever, whatever the situation is, it's going to communicate that up to the nearest new suspense boundary. If there's no new
suspense boundary. If there's no new suspense boundary, we we're not going back to fall back. We just pass it right through it, straight through. But if it hits a new suspense boundary, the new suspense boundary goes, "Okay, I got you.
I'm going to show your fall back and I'm not going to communicate it up. It's
going to stop with me." If you have multiple new suspense boundaries, then it's the nearest one. it'll stop and be like, "Okay, we're good." You know, now
if it gets past all the suspense boundaries, then that's when it hits the transition. And at that point, the
transition. And at that point, the transition is like, "Okay, I'm holding."
So, if you have a bunch of sources that are only read under new sense uh suspense boundary, there will be no transition. It will just switch to the
transition. It will just switch to the next thing and show the loading fallback. But if any source gets through
fallback. But if any source gets through or is above it or whatever, then it will register the transition until it finishes. And the suspense boundary may
finishes. And the suspense boundary may or may not be still visible at that point.
The real transitions with the APIs we lost along the way. Yeah. I mean, can we simplify this further? This is one of the questions I had in my head. Like, do
we even need create async? Now I like create async because it gives me very fundamental markers. But there's no
fundamental markers. But there's no argument really when you have a graph like this that literally every single signal like what if you didn't have create async and it was literally just
create memo cuz these are eager. The
reason we had create async partially was because they needed to be eager because we we didn't want to cause weird cascading problems where like you read lazily read the first async and then waited for it to read the second which
caused the fetch. We didn't want that.
These are eager. So technically
speaking, we could bake we could get rid of create async in this world and literally make every single signal async. If you return a promise, then
async. If you return a promise, then it's just participating in the graph.
Like it just does it. The problem with stores are tricky because you don't want anyone to be able to write to them anywhere. So
usually the way I handle that is have a proxy that says like while we're in the writer have special behavior. The
problem is async context. after they
wait, we lose that context and we don't know if we should be writing it anymore or whatn not and there's no way to restore it. Um, same problem is we use a
restore it. Um, same problem is we use a generator function because if you if you support async anywhere, create async also supports generators, async generators. So technically if every
generators. So technically if every signal could support async or async generators again it's not bad with a signal because you just return the value through. So then like the but so then
through. So then like the but so then like you just keep on yielding and you're fine. But with a store you're
you're fine. But with a store you're going to be doing fine grain updates.
you almost want to call this like the you almost have to call the setter every time like if the if if we had async context there's a world in which we
could unify all the all the APIs so that both async functions and async generators just worked in all uh
reactive expressions um right we wouldn't actually need create async um the one reason that made me like when I first looked at R3 three stay with create async was because for
SSR I wanted to make it pull based. One
of the interesting things is even though this is pushbased SSR was easier to model run once with pullbased so it's like okay well why why don't why don't I just do pullbased um essentially and
create async you kind of need it because you still need to make the promises eager so it's kind of like a pseudo push pull so I needed a marker to be like this is something we care about uh
actively rerunning um so I kept create a sync in um we'll we'll see how it goes as I get into hydration and uh finish with the SSR stuff but there is I I
think async context is a blocker but there is a world in which you do not need a specific create async primitive and literally it's just the behavior of the graph.
Thanks.
How do we support update? It's not going to be a built-in feature. You don't.
I mean, there is a way. You wrap it in a keyed show. If you again, this is not
keyed show. If you again, this is not your happy thing because if it's a keyed show, you know, everything below gets rendered. You make it a new suspense
rendered. You make it a new suspense boundary. That's how you do it. But I I
boundary. That's how you do it. But I I just don't Why? Like why?
Yeah, Keith. Yeah, exactly.
Yeah. I mean, the problem is who's expert enough to actually do this?
And secondly, without the depth, what are people going to get away take away from it? Just the
topics themselves are hard. Like if I had like a nice polished thing that I was like coming in to do, then like maybe we could pull that off. But like
today, like what I I I tried to edit down my stream sometimes and make videos. And it's like instead of five
videos. And it's like instead of five hours, which let's say three and a half hours on a topic, I I I cut like an hour out.
Like the biggest problem is when I'm into the stuff that's like the real like whoa moments. Um it's pretty difficult
whoa moments. Um it's pretty difficult like they I'm not as good at selling it, you know? I I know. I I watch a lot of
you know? I I know. I I watch a lot of live streaming. So, I know like like one
live streaming. So, I know like like one of the best ones is like speedruns like you I watch and they play the same thing over and over again and then it's almost like they cut the the the streamer's content like while they're talking to
the point that like it's more like the dialogue that they have through the talking and less the gameplay except for when they make big progress forward and because they're doing this repetitive
task. I like what do you cut? I I mean
task. I like what do you cut? I I mean it's possible, but I people have in the past come to me trying to suggest this like the content today does not make a bunch of short videos either. It's not
like Theo. Theo is on point. When he
comes in, he's like, "Okay, next topic."
Bang bang bang bang bang bang.
Sometimes it takes 45, sometimes there's an hour and a half and then he cuts it down to about an hour, an hour or 45 minutes and he's good to go. You don't
get to do that with this, I don't think.
Yes. So, it means with async context create sport streams. Yes, you're right.
still be willing to give a try.
I have made this longer obviously because I not have time to make it shorter. That's true. Yeah. The thing
shorter. That's true. Yeah. The thing
with shortening these deep are tips are l meaning I need a lot contest generalist from previous streams. Yeah.
Yeah. I mean, I could make a video of it, but like it's also not in I don't know if it's in video form yet. Like I I I would have to I feel like I would have to record things differently, like be like go time. Like I did that for a
while with this week in JavaScript. I
actually have short I actually separate off the videos for this week in JavaScript for a while and then I realized I was like whatever and I didn't bother publishing them. But I
actually broke them out because I'm on when I go there. I know the topics I'm going to go through and I'm going to give my take and I'm going to do that.
This is not the same sort of thing. Um,
that's why I watch back at 10x speed.
Yeah.
Anyway, today was long on a lot of stuff. Um, let's let's let's let's get
stuff. Um, let's let's let's let's get out of here. I think you guys are good on on the topics here. Let's let's do this. We can JavaScript as fast as we
this. We can JavaScript as fast as we humanly possibly can, which is still going to be long because so much stuff has happened. I'm just going to close
has happened. I'm just going to close down a bunch of these.
Keep on looking. It's like, what did I change?
It's fine. I changed them.
Like you want these examples still to work when you go give the conference talk next year.
All right. Just cleaning up bookkeeping.
Let me look at my notes to see if I missed anything that I wanted to talk about.
No.
Oh, do you know what I didn't do? We
didn't We didn't talk about Jose's talk.
Ah, it's fine. It's not that big of a deal. I wanted to I wanted to do that,
deal. I wanted to I wanted to do that, but maybe I can just find the excerpt that I'm looking for. Um,
let's do that briefly right now and then we'll get into this weekend.
Yeah, I mean it's possible to do something with like a timeout, but like I I it's it's odd to I guess it could be controlled because
technically like the arbitrary like it's new could be toggled so it is possible to opt And the problem is it just has such profound downstream impact that
like now you have to like it's always going to rip out. I don't know. It's
tricky.
Uh yeah. No. Um
let me see what I wanted to Yeah. Let me
see if I got my notes here.
Let's Let's look at this video for a little bit.
I'm gonna I because there's I have some relevance with it that I didn't have before and I so I want to look at this video but let's I have to change the way I'm
sharing my screen. Give me a second because in order to do that I need to share screen and we're going to watch it fast.
Um chat Chrome also shared audio. Sweet.
>> Just by thinking about it. Meet base 4.
>> Imagine being able to build an app with your name on it just by thinking about it. [music]
it. [music] Meet Bass 44, a complete AI creation platform.
>> Seriously, what what what was that? They
literally played the ad and then played it again.
>> Um, okay. Let's go. Playback speed like 1.5.
And then can I move my position? So I'm
like, yeah.
>> All right, let's let's let's go.
>> Joseph, I'm on the React team at Meta and I do not have a UI equals function of C in this talk. So, I got to do the next best thing.
>> Good idea. [snorts]
>> In this talk, I'm going to do a deep dive into the world of React performance.
On the React team, we take a holistic approach to performance, looking at the big picture of where time is spent in applications, and that's we featured in his talk, we added support for concurrency. Concurrency helps optimize
concurrency. Concurrency helps optimize our apps, whether they're IO bound, CPUbound, or both.
But in this talk, I'd like to zoom in a little bit more on rendering performance specifically. As part of React compiler,
specifically. As part of React compiler, we wanted to thoroughly understand exactly how well React is doing on rendering performance. You hear a lot of
rendering performance. You hear a lot of things online and as we all know, a lot of things you hear online are not necessarily true. Uh so we wanted to get
necessarily true. Uh so we wanted to get a really solid understanding for ourselves. So we kicked off what has
ourselves. So we kicked off what has turned into uh a multi-year exploration into Reacting performance. And that has meant diving into the world of incremental computation.
For those who aren't famili update build systems and UI react is an incremental UI system despite what some
>> No, that's Ricky's talk. This is um this is a this is a different talk.
This is this is this is the the React will never have signals talk but there are some important insights from this claim. Now our approach on this was to
claim. Now our approach on this was to create a series of benchmarks that are representative of different real world scenarios. One of the challenges with
scenarios. One of the challenges with benchmarks is obviously that you kind of end up focusing on one specific case and we don't want to you know overoptimize because that's not representative that that one case isn't representative of all apps. So we created a whole bunch of
all apps. So we created a whole bunch of different benchmarks. For this talk I'm
different benchmarks. For this talk I'm going to focus on just one uh but I'll share kind of some of the broader results at the end.
The draw benchmark that we that I want to focus on here is a highly highly highly simplified can't emphasize the word highly simplified enough uh prototyping app. Um I don't know why we
prototyping app. Um I don't know why we didn't call it prototyper. We called it draw. Just anyway we're not good at
draw. Just anyway we're not good at naming things. Um it supports creating
naming things. Um it supports creating text labels and that's about it. And the
UI is very simp very important thing we can take away from this.
Yeah.
So for this benchmark we have a script as like I said this is designed to be automated. We have a script that drives
automated. We have a script that drives all the interactions to create the React logo. My colleague Yan Passins actually
logo. My colleague Yan Passins actually took the SVG file and like was able to extract out the commands from that to actually write this. Yeah, it just keeps on going. So, we're gonna we're going to
on going. So, we're gonna we're going to skip ahead a little bit. You get the idea. It's a logo. Um, now might be hard
idea. It's a logo. Um, now might be hard to tell in there. That was actually to create the full logo at that 5,000 simulating clicks, simulating typing in the in the uh the width and height
fields, changing the X and Y position.
5,000 interactions to create that logo.
Now, the way the benchmark is set up is that each interaction synchronously updates the DOM, but we don't yield to the browser. So, there's no layout or
the browser. So, there's no layout or paint happening. So, when you actually
paint happening. So, when you actually run it, so I was kind of running an emoji just to let you see what's happening when you actually run it, you click run, and the browser appears to hang for a second, and then all you just see the finished logo, right? Because
we're just going to do layout and paint once.
>> This is an important distinction here.
Um I just want to throw out here because [clears throat] um there are benchmarks that uh just tests the synchronous time which is the
easiest thing to measure. Um GS
framework benchmark actually looks for the full paint which is actually kind of really interesting. uh UI bench from uh
really interesting. uh UI bench from uh the creator Eevee, you can actually toggle the mode whether it counts paint or not, which was really valuable early
on for me because on one hand the if you think about it, if you remove paint out of the equation, it's a lot more stable and it should be the overhead of the framework. But the weirdest thing when I
framework. But the weirdest thing when I was working with solid was for some reason we did better when we had the paint in compared to like it doesn't make any sense but how
could like a framework get worse like you think it just kind of scaled relatively or like you know but like solid surprisingly did better on benchmarks and which included paint
stuff and you think well you know that dirties everything up but it's like what are we doing that somehow I I mean And this wasn't a fluke. This is
consistently um I don't know are DOM updates more performant like is this something that could be changed in these other libraries? I'm not sure but it it was
libraries? I'm not sure but it it was just an interesting side effect. Is it
memory allocation actually having a bigger impact when like under like do is there a pressure under higher memory allocation? Do you know what I mean?
allocation? Do you know what I mean?
Like that causes things to scale worse when you add the pain to the layout. I'm
not sure. Not too important. I just it's it is interesting that most benchmarks actually include layout or paint in some way or they don't include them and then people cheat it by using like a request
animation frame or something like or like a like they set their timer on some way. This is like a classic way to cheat
way. This is like a classic way to cheat benchmarks. They're removing this aspect
benchmarks. They're removing this aspect out of the equation which is very respectable. It's just sometimes I do
respectable. It's just sometimes I do wonder if it removes the full story.
Just just just kind of throwing that out there.
>> In other words, this is measuring pure framework performance, right? That's
that's the whole point of this is we just want to see how much how fast can we make those those uh those updates.
Okay, so we're computer scientists. We
can set up an experiment.
>> Don't start your day with a gross piece of plastic.
>> Adding to the 4 billion plastic toothbrushes that get thrown.
>> And the one thing I remember about experiments is that I'm supposed to change one variable at a time. Um so
let's change one thing at a time. Well,
what are the variables to choose from?
In an incremental system, there are kind of two key architectural choices. The
data model and the update algorithm. So
for the draw benchmark, >> honestly, the channel has a choice whether they show ads on the video.
This is an interesting choice for your conference. Like don't get me wrong, I
conference. Like don't get me wrong, I put I do put ads on my long stream content, but like my conference talk recordings, I don't
like if I could because like yeah, I don't know. It's an interesting choice.
chose a a data model approach that a lot of folks in the community have asked about. We stored all the state all the
about. We stored all the state all the data for the app in a single state value and then pass that down via context. So
component >> reads the data for its entity from context and then accesses whatever properties it needs.
>> Right? Might have heard about context data context selectors or things like that. This is kind of the rough idea,
that. This is kind of the rough idea, right? We're just going to have this
right? We're just going to have this giant pack of data in context and we'll kind of pluck out at each component. The
one thing that I'm worried about about this >> is this doesn't say anything about like the different solutions like context is a container. You can use it as a
a container. You can use it as a dependency injection or you can use it to set state and rerender the whole tree but it doesn't actually say much set this context
and then just pass down the ID to each entity. Okay. So that it can in turn go
entity. Okay. So that it can in turn go and go actually read uh this data from the context.
>> Okay. And again, this is just a giant hashmap sort in state and then set as a >> and then they literally just >> that's our data model.
>> It's one state variable for the whole thing and then they trigger everything.
Been a while since I watched this. I
thought the takeaway was they can speed things up far greater optimizing data model exam, but they never tested signals with optimized data model. Yeah.
We never showed those results. Yeah, I
mean that that was the takeaway, but there's a couple things I think we should take from this just as fellow data fellow UI web UI scientists.
>> As we can see, this is basically a giant stress test for state and context as the representation.
>> Is it a giant stress test? See, to me, this feels like a giant stress test for um React's diffing capability because if
you put everything in a single giant U state, like did I did I get that right?
Use state new map entity set. If if if this doesn't seem like a test of the data, it seems like a test of React's ability to diff more than anything
because every time you change it thing, it needs to diff the whole freaking tree map stored in state and then set as a value in context. Okay, so that's our data model. As we can see, this is
data model. As we can see, this is basically a giant stress test for state and context as the representation for all your data in an app. How fast can we make it?
So this is the variable part of our experiment. So we chose to experiment
experiment. So we chose to experiment with different update algorithms given the same data model. React react
compiler and well we have the ones in a second. Let's look at the first let's
second. Let's look at the first let's just look at the results of these two first. So our hypothesis was that react
first. So our hypothesis was that react compiler would should hopefully fingers crossed we spent a lot of time on this thing be faster. Um but it could only be so much faster right because we're changing the context and every single component pulls from that context. So
how much faster can we really get when every component has to update at least somehow right? If this is your data
somehow right? If this is your data model a lot actually because React compiler by memoiza by memoizing
is going to be able to um like compared to the naive rer like this is like like this is like kind of terrible. It's like
rerender everything baseline. So if you can add memoization you're going to see significant like now if someone went through and handmemoized even that would be challenging here because the data
structure itself is one use state do you know what I mean like so your expectation here is actually this is one place where the react compiler should actually have a positive benefit >> okay so baseline first the results for
react now this version has minimal manual memorization >> remember this is 5,000 interactions taking just just shy of 4 seconds so this is the best case scenario And here's compiler.
>> Yeah.
>> Right. [applause]
>> Yeah. Not bad, right? We're pretty
happy. Like, okay, we could probably keep our jobs.
>> Yeah. I mean, this is really the best case scenario for the compiler because essentially non-memorized React. I I did I did in the JS framework benchmark um
the similar thing where I took like it wasn't even non-memorized like I had to like it because structurally it was smart. We did hoisting and stuff, but I
smart. We did hoisting and stuff, but I just removed all the memo calls and compared the compiler to not compiler.
And yeah, it went from two, it was actually slower, slightly slower, uh, naively than the transitions version that was hand optimized. And then the compiler brought it as you can see back
down to where you know almost where as we showed earlier in the stream almost down to where the hand compiled thing was which my conclusion was the compiler doesn't make code faster than hand
memorization or it's about the same but definitely makes your code faster. So I
think this is this is confirming that the compiler does memoization reasonably well. Right. Sorry, I didn't
reasonably well. Right. Sorry, I didn't pick it up the first time. Watch the
talk that they literally jam in one use state. That like that that almost makes
state. That like that that almost makes like all of this mute. Anyways, let's
keep on going.
>> So that's awesome. How though? What
exactly is happening? I just said every component has to update with it new context. So how did it actually get
context. So how did it actually get faster?
So in React when we render our UI creates a tree of what we call fiber nodes. The fibers correspond to
nodes. The fibers correspond to components. So you've probably seen
components. So you've probably seen diagrams like this in the past. And our
mental model is that oh the component is rerendering, right? So if draw updates then you have
right? So if draw updates then you have to propagate that value down and that means updating.
>> Yes. Yeah. Yeah. Yeah. Right. They use a mutable map rather than a plain object.
Must be copy the map on each update.
Yeah. Yeah. Exactly. Like it's it's basically immutable at that point because it needs to check everything anyway like
Yeah. I mean well okay [laughter] the
Yeah. I mean well okay [laughter] the benefit of the of a of of the mutable map there is one slight benefit is although not in this I think because
it's going to be primitive values it is possible to keep references I guess like do you deep clone or shallow clone you shall clone anyways yeah who am I kidding you you would shallow clone so
yeah yeah you're right this is odd I mean it saves you from shallow cloning I think you can set the same I I think you can set the same map each time and still trigger actually though I
don't know if the compiler would catch it. That's a good question. I don't
it. That's a good question. I don't
know. But yes, it seems the fact that it's a map might be like a small optimization or basically weird non.
Okay, so let's continue >> everything.
Well, with React compiler, that's not really what's happening.
>> No, >> for example, we only have to rerun the map if the entities change, right? if if
some other property of context changes, we don't have to rerun the map operation, right? The compiler is
operation, right? The compiler is actually splitting up this component into smaller pieces of logic. And
actually, we do the same thing on the item, too, right? Breaking it up. So,
for example, we only have to update the style property maybe if the width and the height of that entity have actually changed. And then, of course, we do this
changed. And then, of course, we do this for all the other components. The funny
thing is if anyone's seen my my talk um this talk uh there's I actually do this via slides where I'm showing like naively if you have a shopping cart and you have state up in the app and you
have button down here and the shopping cart here you know through the header user main and you do a mutation here and the state's kind of shared up high so it's in both places like in a context you end up rerending everything and then
the compiler lets you or your memorization lets you follow one path because even if you click here and trigger it up. You don't need the rerender down this way because there's like nothing changed on the buy button side. It's only on the cart side. Um, so
side. It's only on the cart side. Um, so
there's like a there's it's there is like a parallel here. Oh. Oh, sorry. You
guys can't see my my talk, right? Um,
sorry. I I forgot that I'm only sharing this one this one screen. Um, never
mind.
Contact value itself has to be recreated. And I think the map was a
recreated. And I think the map was a field in that value rather than the value itself. Fair enough. Yeah. Okay.
value itself. Fair enough. Yeah. Okay.
>> So, in the benchmark, if we've updated a single entity, sure, the context invalidates, the entities invalidate, we have to rerun our map, but then most of these entities are just going to be a no and things kind of basically kind of stop there.
>> What what I love though, this is showing like going down the one path, right?
Essentially, like that that's what I I was trying to highlight. Like you can memoize out the
highlight. Like you can memoize out the things that don't need to change, but you still go down the one path. And we
actually spend time updating here. So as
you can see as the number of entities grows, we're just mostly skipping the work on the on for all those entities and just focusing on where we need to.
So this is how react compiler is able to make this type of application faster.
>> So >> this is best case. Yeah.
>> React compiler can significantly improve the performance of context based apps.
>> Now this >> or it can significantly improve the performance of all your state in one
state variable ho hoisted high apps.
Right? Like context is no one bothers with prop drilling. So context is the most common way of doing it. But the the actual takeaway is that like if your state is hoisted high,
you React ends up having to do more work. Memorization allows you to follow
work. Memorization allows you to follow the branches.
>> Recurring question in this talk. Can we
go faster?
>> Yes.
>> Yes.
>> She's cheated. She's seen the you saw the dry run. Doesn't count. Um, now
there's been a lot of noise about signals in the community. What if we use signals? I heard they're fast.
signals? I heard they're fast.
>> The world's first titanium non-stick pan. Blow your hair back.
pan. Blow your hair back.
>> I'm Mike Miguel.
>> So, we built a prototype, a brand new approach to React. Groundup rewrite of the runtime using signals. Or well,
>> but I know the answer to this question because I already talked to them. They
did the same thing. They used one signal just like one used state.
Let's continue.
>> Not quite signals close. Pull based or lazy incremental computation graph. We
extended React compiler to emit the code for this translating existing React components and hooks to use this new runtime. It's pretty cool, right? I'll
runtime. It's pretty cool, right? I'll
talk about how this works in a moment.
Signals. We've heard so much about signals. I'm sure you are all really
signals. I'm sure you are all really really excited. You excited?
really excited. You excited?
>> He's playing it up so much, but it >> the results. Want to see the results?
Okay. Yeah. I'm just as excited to show them to you and not for the reason you're expecting.
>> Well, let's think here. If I had signals, one signal updates a state and I didn't have the React compiler, my expectation is the performance would be the same as
baseline React because I'm updating one state and then I have to go freaking diff everything basically.
Yeah. Oh yeah. No deep tracking. Yeah.
Yeah. Yeah. So my expectation is that it would be relatively the same performance as normal React. If it's faster than normal React without the compiler,
that's actually very impressive.
Our results so far.
That's impressive.
Yeah. So this is when we started to question our life choices. Maybe we're
just not cut out for this. And then we regained our senses. We double checked.
Okay. What if we just made a mistake because you know that happens. So we
took a signals based virtual DOMless library that scores well on popular benchmarks and we >> solidjs in case anyone was wondering draw to that library. The specific
library doesn't really matter as we'll see in a minute. Again this is an experiment. So we're changing one
experiment. So we're changing one variable at a time. So we're going to change the rendering you know the rendering algorithm.
>> You he is being honest here. It's just
it's it's difficult to tell because what's happening is um the ver he didn't want to change the shape of the solution. If you have one use state, you should you'd have one
signal right?
The library doesn't matter. I mean, in fact, if anything, um I the fact that their react forest was so much more performant than I would
have expected suggests that they did a lot of optimization um in the way that they propagated their change there, which is pretty impressive actually
because like it should be the same speed as normal React, not the compiler.
Right.
We ask ourselves if we made a mistake while ignoring the biggest mistake.
Well, I mean it's it's it's this from a very like procedural thing there. The problem
is you can't when things are different enough comparing apples to apples doesn't make sense.
But but like I think I think they did pretty good job here, right? Like from the perspective of like getting that much performance of by literally just
switching from use state to to use signal is actually kind of incredible.
Like I don't know what their signal implementation is, but I am very impressed because they didn't get any of the architectural gains, right? They didn't they didn't
gains, right? They didn't they didn't get to drill straight into the into the thing that changed. They literally are still rerendering the whole tree. Um,
I mean, okay. I I I I wonder if they if they were using something other than map, like if they were maybe using like a smart reactive map function and that kind of brought back some of the
memoization and that explains the difference. Like that's my guess. I
difference. Like that's my guess. I
wonder if they use something like our four component. So they had one signal
four component. So they had one signal and a four component and that would that would actually check for my head that might that might be that might get you in the right kind of zone.
>> So which means keeping the data model the same. Um, so we use the same
the same. Um, so we use the same approach, state passed down via context.
And I want to call out if you were to go and use a signals based library, this isn't how they'd recommend storing your data, right? And so it might you might
data, right? And so it might you might say that, oh, this is an unfair, you know, comparison. But the whole point is
know, comparison. But the whole point is we're doing an experiment to compare given this data model, >> right? Yeah. And so he he knows what
>> right? Yeah. And so he he knows what he's doing. He just doesn't like it it
he's doing. He just doesn't like it it doesn't quite highlight um like how absurd this is. Like do you know what this is like? Have you ever
seen uh DBimmon? Um it's the there which is like the Ryan Florence demo uh where they do the stuff on the screen and React like killed Ember in Angular. If
you ever look at the knockout implementation, we should do that for fun sometime on stream. It's literally
one signal and it makes no sense, but whoever implemented it couldn't care less. Like I guess, right? Like you
less. Like I guess, right? Like you
would never ever do this like ever like in 10 years like this. So it's it is an odd thing because the whole point is signals are to be fine grained and this
is not fine grained.
So it's like you almost go like is there any like the fact that they saw the other improvement is confusing actually like maybe that's why they thought they needed to do another comparison because
of the their signals like signal one signal should be identical to one use state in React that they're both atomic.
They're both immutable. They're the
exact same thing.
add a fancy compiler to React should be faster. Add um maybe a simple four
faster. Add um maybe a simple four component should be slightly faster, you know, than this the signal. But it it's essentially just one has more memorization and one has less
memorization when you have a single signal because this isn't about Yeah, I mean it's interesting. This
isn't about idiomatic usage in other libraries. The the problem is the
libraries. The the problem is the problem is it's difficult to talk about it because like it's like here we have a solution that works like
this and then be like for the sake of being consistent with what we already do, we're not actually going to use it the way anyone would consider using it.
So, it's like it's it's kind of like you're not really giving it a fair try, but let's continue.
He knows what he's doing. He doesn't
want you to take that part as a takeaway.
Yeah, I'm gathering that they they they they they used the memoization in the for loop like in just the loop essentially like like the math function.
That's the only place that really makes sense to to add it.
It it it does, which is why I we built stores. Um really people got to see my
stores. Um really people got to see my latest conference talk. We um We do it today in solid 2. If you use create async store um from the router, it it
handles it in a smart way.
Okay, let's keep on going.
>> What performance characteristics uh do we see given for this pre for this rendering model? So, how's it look?
rendering model? So, how's it look?
Okay, maybe maybe they're really fast, right? Signals are fast. All right, our
right? Signals are fast. All right, our results so far.
>> Yeah, they did a good good job with their minimal system.
So what's happening here? Well, in both our incremental prototype and in a typical signals, you end up with something that looks very vaguely like that.
>> I got tired of drawing boxes and aligning them. So I'm sorry that's the
aligning them. So I'm sorry that's the best you get today. On the left, we have our context, which really there's a state going into the context, but you get the idea. There's one giant input, and then we have a whole lot of derived computation. And then over here on the
computation. And then over here on the right, we have our places where we're going to use these values. These nodes
in the middle, we only want to compute them once, right? If a node has two inputs, we don't want to compute it twice. first with only half of the
twice. first with only half of the inputs updated and then again when all the inputs are updated. We want to make sure we compute each node at the right time only when it inputs have been updated.
So there's a very specific set of nodes in this case you need to pay close attention to this uh that are affected when this context changes. So watch
closely it's all of them. Um okay so so first we'll just mark them as changed.
We're not actually rec.
>> Okay. Okay. Okay. It's doing
notifications.
>> We'll go walk this tree and say give me the latest value.
>> And so we'll actually pull back along this tree. And so that if there's a
this tree. And so that if there's a fancy algorithm here, you actually want to go first and then if one of the nodes doesn't change, you kind of stop. And
it's fancy. You can look it up on blog post and stuff.
>> Okay. See, this suggests though that he has multiple nodes, not one signal, which is interesting. So we don't actually know this for It's funny. When
I talked to Joe, he made it sound like it was basically just like one thing, but this suggests that it's not one thing. Um I mean this is true but which
thing. Um I mean this is true but which is the reason this is relevant is because this is what R3 solves. Um the
because it it actually does it doesn't do the double coloring but let's continue.
>> Maybe Chief can explain it to you. Um
we're just going to skip over this, you know, high level here. Um but the point is we're actually visiting the graph again. So we're visiting the whole graph
again. So we're visiting the whole graph once to mark things as changed and then once to recmp compute. The biggest
problem is he he literally marks the whole graph, which makes me think it is like one signal. It's like he it's derived. It's basically one signal that
derived. It's basically one signal that deres, right? He he it can't fork. It's
deres, right? He he it can't fork. It's
not like projections essentially like or store. It's one signal and everything's
store. It's one signal and everything's derived from that signal. So you end up doing all the work again, right? Um
so when you start thinking about it, you're like that actually doesn't sound fast, does it?
Okay. So the signal style poll approach wasn't giving us the performance that we wanted.
But again, >> we do notify because there's the potential.
>> We actually got to the point that I just described a couple years ago before we even shared React compiler at the conference last year. The existing
incremental computation literature wasn't getting us faster. Um, but we have a lot of experience with incremental systems at Meta. In my 11 years at Meta, I've worked on Relay, which is an incremental in-memory database. Relay compiler, an incremental
database. Relay compiler, an incremental compiler for GraphQL, skip, which was an entire program for incremental computation. The compiler for skip was
computation. The compiler for skip was guess what? Written in skip as an
guess what? Written in skip as an incremental compiler. And now also
incremental compiler. And now also working on this. And in addition uh I've had the privilege to look closely at other incremental systems at meta like hack and flow.
And one thing that has stood out across all that experience is So, fast forward at least a year, numerous prototypes created and thrown away with, I got to say, amazing engineering work, incredibly clever
designs that just did not pan out until we hit upon uh a variant that we call React fur. It's a novel approach to
React fur. It's a novel approach to incremental rendering that uses a hybrid eager lazy algorithm. Now, it's
important to note this prototype is >> hybrid eager lazy algorithm.
As I said, it makes me think of uh like he's describing something like push pull, but I'm gathering it's not um it's not marking all the nodes the same way.
As I said, I would not be surprised if if it's the work on R3 is actually very similar to this where you have local
push push pull or like but mostly work off some kind of like height graph >> not support all the features of react but it does support a lot of common patterns more than enough to render the draw.
>> Honestly, I don't think signals are general purpose. Um they aren't super
general purpose. Um they aren't super general purpose because the consistency guarantees are uncommon but it is possible
you they are general purpose and like the they didn't like it's not like you're walking specifically like a BOM node or something like they they they you can apply them anywhere right like
that. So
that. So >> yeah, >> that said, before you get too excited, this is a constrained prototype. It is a long way away from shipping, >> right? This is which is something I
>> right? This is which is something I always have to remember when I when when you build the prototypes, they're always faster. Like I I used in this the
faster. Like I I used in this the benchmarks in Solid's repo, you'll see I actually have a version of Solid 1's algorithm without all the extra features. So I can test it to benchmark
features. So I can test it to benchmark against other things. And then inside Solid, I have the fully version because it's slower. there's constraints,
it's slower. there's constraints, there's limits, there's things that I couldn't do, but I still needed the pure version so I could actually benchmark those signals reactivity against other libraries because you know without the extra features so I could know that the
algorithm is fast. So a lot of times with these kind of like limited things the the real performance you get out the production version will be slower. Um
but we wanted to see you know if we do kind of strip things down to to the core how fast can we make it? That was that was the idea here. So this time you're probably excited for the right reason.
Yeah. Yeah, couple years of work to get there. We were pretty impressed. Pretty
there. We were pretty impressed. Pretty
happy.
>> If your workout still looks like this, you need to start training with AI. This
is AMP. It's
>> now a new React code name, a novel incremental algorithm. You probably have
incremental algorithm. You probably have a lot of questions. You have questions?
>> Yeah. Yeah. Okay. I guarantee the one you're >> This is the same thing as the data store guy. He has the fastest single
guy. He has the fastest single implication because it doesn't do dynamic tracking. I bet you that is
dynamic tracking. I bet you that is exactly what this is.
I would not be surprised because the problem is if you can't change the shape of the uh of the dynam of the dependencies then you can basically fix graphit and then you can basically
you a whole bunch of questions around like what happens at runtime can reduce which can you're so on the money dev I bet you that's what that is
okay still very impressive to see this kind of improvements.
But yes, I think you're right.
Yeah, sounds kind of like that's based on height stuff. I have computer eager and derives lazy. Yeah,
if they're in the mindset of come sides of depths, then why not bother run why then why bother runtime tracking? Well,
Marco had the same question and they actually ended up with runtime tracking.
Um but they didn't want to. Um the the the biggest thing is it's difficult to do things with like mutable reactivity in stores with runtime tracking. It's
it's harder.
You can shrink the graph down to user if you don't do traffic. I I my guess is that's what it is. I that is so freaking Yeah. Yes.
Yeah. Yes.
The overhead you're seeing at this point is just like React's own overhead of Yeah. I I think you're right. Let's keep
Yeah. I I think you're right. Let's keep
on going. We don't know. We're
speculating, but I think you're right. I
think that makes a lot of sense because we it's not like we haven't tested some of these things ourselves. So, let's
keep >> thinking of is not this. That's right.
We're just going to move right along. We
got we got to make things fast, people.
We got to keep going.
comp and a way to publish updates to a few records at a time.
>> And then we'll write a little hook that lets us, you know, read and subscribe to the store. Basically, I'm writing my own
the store. Basically, I'm writing my own using external store here is a bunch of to-dos, but you get the idea.
>> All right, so again, our results so far.
And now, oh wait, that's right, I forgot. So I'm not this is a comparison
forgot. So I'm not this is a comparison now. We've broken the experiment, right?
now. We've broken the experiment, right?
Right? Because the whole point of the experiment is you change one variable at a time. We're changing the wrong
a time. We're changing the wrong variable now. So this is not an
variable now. So this is not an experiment. This is just a comparison.
experiment. This is just a comparison.
But still, what are the results?
So is this just like it's funny that they decided to share this because if this is what I realized that this was my conclusion here, I wouldn't have concluded that I don't
need signals. I would have concluded
need signals. I would have concluded that I was doing the wrong experiment.
Like is isn't that the conclusion here?
If literally you could just pull an external store into React and beat every possible thing you've done, then you're probably looking at the wrong thing,
right? Like
right? Like cuz I mean I I'd love to see React with just external store versus without the compiler here. But the the gist of this
compiler here. But the the gist of this is if you aren't rerunning the tree because you're literally not using context or using a single signal to propagate everything derived. If you're
literally not running the tree and instead you just enter the React components in, you know, the 5,000 different locations or whatever, then that is going to be faster than any of
the propagation stuff. Basically, the
propagation doesn't matter here, whether you're using one U state, one U signal, or whatever. In in the scheme of things,
or whatever. In in the scheme of things, you go pick up Jotai or something and and some kind of external storage thing
and all of this doesn't matter, right?
Yeah. Pick up Redux. Why not?
Yeah. Yeah. Yeah. Perfect. Thanks, Mark.
Yeah. Even redux the cost of running ends callback selectors to figure out the components to rerender less than the cost of rerunning all the components.
Yes, I know the legend state guy. Yeah, cuz
now he's like, yeah, yeah, yeah, yeah.
Doesn't external storage still not work properly because of concurrent mode?
Yeah, I mean this is what Tanner asks at like literally every chance he can publicly on stage, but you know what I mean? The funny
thing is me and Joe are in a lot of agreement when I look at this talk so far. He basically said that there's a
far. He basically said that there's a more optimal way to do signals which I agree we've been working on it and that this propagation kind of like reactivity benchmarks doesn't really matter all
that much compared to just being able to pull the stuff out into a store. So
let's continue.
Oh yeah, and we didn't we did we barely, you know, we just we slightly rewrote things to use a store, but we're not like dramatically rewriting the code. um
and in particular because the the subscribe function, we can do very targeted updates in this store.
>> Okay, >> so why is this so fast? Well, if we go back to the incremental computation graph that we looked at for kind of the signal style graph. Um the issue was that the context was kind of upstream of everything. And so when that changed,
everything. And so when that changed, too much work downstream got invalidated. When we shift to a store,
invalidated. When we shift to a store, it's kind of like we can see inside that context. And so now when one entity
context. And so now when one entity changes, we're just invalidating way less work and way doing a lot less doing a lot less downstream.
But far enough along in the talk that you know what I'm going to say next. Can
we make it faster?
>> Yeah, there we go. All right. In
particular, what if we combine these ideas? Because I just said we changed
ideas? Because I just said we changed the rendering algorithm and we changed the data model. Like, can't you do both?
You can. So, React for similar external store. Here's where we are so far.
store. Here's where we are so far.
>> Yeah. [applause]
I did not believe that number when I saw it.
>> See, I do because here here's the thing.
He's created an optimal signals implementation and he's and he's doing looks like some kind of
finer grained like I don't even know what his external store fine grain update things but when I look at this
the the real crazy part is if you get a store like a a proxy store in like solid it is already granular.
Each property even nested is a separate signal essentially automatically. So
when you update the thing only the thing updates right and what's crazy about this is in order to get to this they needed to externalize the data
structure that when they say they use a different data structure they actually architect they they need to externalize it so that it wasn't in React's component render model and then they had
to make the render model um essentially be smarter so it doesn't diff as much use like a kind of almost like a push pull something similar to signals.
We will never see the results of this, but my suggest my guess is that solid with a store or spelt with a with their
state or whatever reactive system today if you just went walked over picked up those solutions is going to get numbers like the best one in this
chart today with zero effort. That's
just like where we are today. Um maybe
maybe slightly slower than this, maybe like a tiny bit because they've like super optimized this, you know, but generally this is like I don't see an argument
against signals here so much as like this is an argument for signals like is he he's basically like the biggest difference between between a fine grain renderer and react
is that whether you pass the signals through the component components like props or you put them in an external file like a store, none of the components rerender. Like it's literally
components rerender. Like it's literally the same thing. Like you can go like, okay, I mean maybe there's a couple extra getter calls, but like the the difference between saying putting it in
context or putting in external store, there's no difference, right? The the
problem wasn't that it was in context.
The problem was that there was one use state instead of like, you know, a thousand use states. So like
I don't know is that a weird takeaway to have from looking at this that basically separating the rendering from the state basically the like react antiattern is
actually the solution to their problem.
Basically not being react is the solution to their problem like I'm checking chat here if you do this talking about how signals
that work for eight years. Yeah,
maybe minus the dependency tracking for extra speed. Yeah, it's an argument for
extra speed. Yeah, it's an argument for using signals for state management, but I I like it's it's more than that. It's
an like you could that's why the legend stick guys are excited but it's more that's in the react ecosystem. It's an
argument not to use react because you get this benefit by not even using by just natively using a signals based framework like this is this whole thing
is just a nonissue. It's like you is it a external store is it your component state same thing like the whole concept category of problem just doesn't exist.
It's it's kind of crazy. I mean, it's it's I'm not saying React's bad model or anything. Generally speaking, like it's
anything. Generally speaking, like it's different and there's benefits of having this like rerender kind of thing. It it
makes things simple, but like this is not the conclusion that I was expecting, right? Like this is like like
expecting, right? Like this is like like if if stopped at React fur I you know, but then it's almost like everything from here invalidated everything that happened before. As you said, it went
happened before. As you said, it went from experiment to comparison. And this
makes you start going, well, does this like It mean it means that react okay it means that react is going to change its identity over the next few years if this
is the future if this future is to isolate data from rendering which has like never been the react approach it's sure it's not going to use quote signals
but it this is a direction that suggests that the compiler is probably going to be the one kind of masking over it a bit maybe but this is a suggestion that
reacts rethinking their fundamental render model internally at least you know like they'll give you the one appearance on the outside but internally it's actually working in a completely different way because
like isn't the whole benefit if the whole benefits comes from externalizing the stores and you don't want people to have to write everything as an external store well if they first of all if they're writing it all as an
external store then that's very different it's outside of the component and if you come up with a way to make people write it as if it were an external store. Then you're kind of like
external store. Then you're kind of like cheesing the physics.
Yeah, this is building.
It's kind of scary. I'm going to do it from the slides. I think it's also worth noting that the most odd context of this talk is about the traditional hoist up state pattern react.
Yeah, it's it's interesting.
And also the React team is a dead set that the only valid state management solution is React itself. They agree
external state is used, but they want to acknowledge it. Well, it's because they
acknowledge it. Well, it's because they want to control the async. They want the features to work. Like, don't get me wrong, I'm just as greedy.
It's just like like from like a solid perspective. We the difference is when
perspective. We the difference is when React came out, they were like here, use whatever state management you want, whatever models you want. We're just a render library.
Solid's like we're signals library.
We're state library fundamentally. So
like when you use solid, you you're using our state. You can use it in the component, you use it outside the component, you can use it wherever the hell you want, you know, but when you use solid, I mean there's like a trick where you can like sync it with an
external source. We do have a mechanism.
external source. We do have a mechanism.
So you can use like MobX instead of solid signals. But generally speaking
solid signals. But generally speaking like and it works actually pretty natively. But what I was trying to get
natively. But what I was trying to get at is like when you use anything in solid you're using our reactivity.
That's the gotcha. That's the
coloration. You need to be using signals. You know they might be hidden
signals. You know they might be hidden behind proxies or other stuff. Like if
if you if you make a state solution, a lot of the state solution people like stuff like Redux isn't bad because it's like literally a single immutable thing and then we just have it at the edges and then it becomes one of our stores
and then we do fine grain updates off it. Um but like people like Jotai, the
it. Um but like people like Jotai, the Jotai guys were like let's make Jotai for solid and then they're like yeah that's not going to happen. like it the atomic state management like that makes
no sense in solid because you' just be mapping one to one with signals like you get no benefit like zero benefit from that kind of thing you could like make a model that like look
like Jotai's APIs but you you'd be better just to wrap our signals in their API shape than actually use anything that they do right and I think I I think
like so in a sense right from the beginning we said we own the state and not just your global component state we own all your state and that's why we have no problem like walking into this
async world you know where and performance where this is kind of like you know I mean don't get me wrong there's a there's a conflict with that like when you if you're coming in and you're like I have my own state update
and I want to integrate with your system there's like a little bit more of a of like an issue like you know I did work a bit with Tener at one point in terms of like bridging that gap and you know you need specific solutions perhaps and I've
been working on making those easier with our diffing functionality and stuff, but but it means that more likely if you're someone like Tanner and you come in and you're looking at stuff, you just take
your APIs and wrap our stuff with it.
Essentially, it's all solid. Like solid
query uses create async or create resource today, but you you get what I'm saying? Like it's literally you're using
saying? Like it's literally you're using our state. It's like which is not the
our state. It's like which is not the case in React. And I think they're realizing that they have to have a solution here so that third parties can just like use their state and have it
being concurrent safe. See, it was really easy for us to be concurrent safe or whatever because literally every library in that uses solid essentially is using solid like they're already
using our state. So this is this is all ties together and makes sense. But it's
also kind of like if if the data now we're in a place where react is the render framework the compiler they have their own state solution like for external state store
like I mean it's fine it's just how much has the like what react is identity changed over time because because of this like I mean it's tricky
the fact that they've been able to persist and continue to evolve is is is obviously amazing thing but It's also kind of like you're like we've had enough time now to see that maybe there's like more directed
solutions that kind of accomplish like similar things but were already built this way to begin with.
Might as well just say for plus store emitting react altogether maybe.
The only way react command current running is if it owns the state that's why it uses has it limitation as an external state it's faster is a big deal. Yeah. Yeah. Yeah. Yeah. No, this
deal. Yeah. Yeah. Yeah. Yeah. No, this
is huge.
Now, now they want but my guess is they'll want to control that external state. So I don't see a change of render
state. So I don't see a change of render button in React components. Yes. Yes.
Yes. Yes. Exactly. I I'm just catching up. You probably said that while I was
up. You probably said that while I was spieling off five minutes ago.
Yeah. You
Mark knows what's up. Yeah, links don't work in YouTube chat. Yeah, that's
unfortunate.
Top few. There we go. There's the info you need.
They did Joe diol. They call it nano stars. Okay.
stars. Okay.
My I talked to Mark about this and my guess is they're going to create something kind of almost similar to one of our stores where if you if you the way I I talked about the stream today
the way that I handle concurrency of having managing both the pending and committed values on a per field or per
signal level in our stores essentially is how I imagine them doing this. And
then they can basically associate um these states with some in-flight transitions and basically have multiple realities kind of sitting on top of each other. And then you as long
as they provide the interface like the getter setter interface it should work.
I conceptually they I don't know how the subscription aspect would work in terms of updating for granularity but conceptually I I I
see something along those lines.
The difference is like when we approach it all, we were doing out a single s single signal at a time and then a storage is just a comp composition of that.
Yeah, they already have a Yeah, I'm not surprised has an international deal to full sync render but because they concurren now a prototype that does work current. Nice.
Okay.
And Van said the prototype is essentially an async signal if you squint at it. Yeah. Which is probably why Mark is here today. like
you know I I didn't get too much into the code implementation but essentially these are kind of like an individual reactive or I mean maybe not the
tracking bit but like like I don't know how the subscriptions happen but essentially from like the right side of having basically
kind of like multi- um committed versus pending value piece that can be you know exist at the same time. Anyways let's
let's finish this out.
really hope it's right. Um, okay. Now,
this is the point where we have to go back. Remember how I emphasized the
back. Remember how I emphasized the highly highly highly like triple highly super simplified at the beginning of the talk? This is a highly highly simplified
talk? This is a highly highly simplified benchmark.
>> Um, that makes it really great for optimizing the innermost workings of the rendering algorithm, but it also makes it less representative of real world scenarios. So, we also created other
scenarios. So, we also created other benchmarks that have a richer set of components and a more realistic data model. The draw was basically a key
model. The draw was basically a key value store as we saw. Um, now these other benchmarks are again not all the way to a real app, but they're much more representative and the results there were not quite as good. So this is a different benchmark again with React
compiler and a store and then react fur and a store and we can see that it is still a performance improvement but not quite the same magnitude as we were seeing on the draw app. Right. This is
just as we increase the complexity of our app, the rendering algorithm just becomes less critical to the performance and there's only there's just only so much we can actually make faster. Yeah.
>> And there's another aspect too.
>> Yes, it's faster but it's not the full react, right? And from a couple years of
react, right? And from a couple years of experiments, we know that adding back some features that people rely on every day with React would eat into these wins.
>> All right. But
>> yeah, I mean, basically they what they're saying is they may or may not ever do React for which I think is fair.
Like they're saying the compiler is close enough and that the store is the pole in my next.
>> No, no, no, no, no. It's time for a break. Um, okay. There's so much time.
break. Um, okay. There's so much time.
There's so much more that we wish we could time to get into. model is just as important, maybe more so than the particular incremental algorithm we choose.
>> Yes.
>> Second, our results suggest that domain specific approaches to incremental computation can be substantially faster than traditional incremental algorithms.
>> This I'm not as sure about because he he showed that, but then they're saying fur is domain specific,
but then he went on to say that fur probably doesn't make actually that big of a difference.
So in my head this makes sense but I I I'm not sure how much it applies in this case >> like signals which are pull based or or generally lazy
>> but also as we saw with that last slide improvements on one benchmark don't necessarily translate to other benchmarks and in some cases or even if they do they don't translate to the same magnitude right the draw benchmark is almost too simple and a richer set of
benchmarks trust us more cases worse during those couple years of experiments we had actually identified a few different ideas that looked really promising on benchmarks when we ported into real just
ultimately these numbers we really have to actually push them further to get into real app and see how they're going to things are going to play out.
All right, I can't take another ad.
We're just going to close this one down.
Um, all right. Uh, let's go back to sharing
all right. Uh, let's go back to sharing my screen.
Uh yeah, funny thing is he called key value store peak. Yeah, rally on key value
peak. Yeah, rally on key value observables. Yeah, the benchmark might
observables. Yeah, the benchmark might be simpler, but it let sounds enterprise wait for react. Yeah, I mean that's the g that's that's the game that always plays it. It's fine. If I had the most
plays it. It's fine. If I had the most popular solution, I would definitely keep on telling people like one day and betting on the future because honestly it's the hardest thing to do in the
world is to change or migrate or do something new, you know. So as long as you know React will do it one day, you know, or almost do it one day or whatever, it's enough for most people.
So yeah, I mean hold the wine, you know, do do what you got to do. I think that's I think that's fine. um
said the omnive adjapse he actually got.
So I I don't know if I if that's what he said. I think he just meant that the the
said. I think he just meant that the the difference wasn't as wasn't as understood.
Anyways, yeah. Uh I wanted to do that. Uh let
yeah. Uh I wanted to do that. Uh let
let's see if I got a couple more things to to look at here um before we head out. But let's see. What do we got?
out. But let's see. What do we got?
Let's do a quick this week in JavaScript um home profile. Give me a second. I'm just
profile. Give me a second. I'm just
going to actually switch in my head here while I get a couple tabs open.
It's been it's been a it's been a sweet minute since we did a stream. I as I said I went and did a conference talk, did a few other things. I think the last
stream we did was on October 24th.
So that's our threshold.
All right, let me scroll back to October 24th.
All right, looks like solid stuff is good to begin with. And then one more duplicate.
One more section.
Bookmarks.
Oh, wow. That's funny. We talked about the directives thing on stream, but then it like blew up like after the stream.
So that's actually where we start from.
But I'm going to start with Salt. Let's
do this and talk about this week in JavaScript.
Okay.
All right. So, let's go. Let's go.
Um, yeah, I'm going to start with some solid stuff because it's probably the easiest stuff. It just it's first big
easiest stuff. It just it's first big solid news. We hit 1 million downloads a
solid news. We hit 1 million downloads a week, which is huge for us. Um,
hopefully we're still doing that right now. Let's see. MPM SolidJS.
now. Let's see. MPM SolidJS.
Be really sad if we hit that threshold and then uh it just drop right off.
Okay. No, we're still climbing on this really beautiful trajectory here from the last six months. So, we're almost at 1.1 now.
I I we were at 300,000 a year ago. Um it's it's been incredible growth, especially on a year where we haven't released anything like major.
Just been doing fixes on solid doing you know small bug fixes on solid start.
I've been very deep into my research.
I'm hoping, you know, people are just realizing the the power of using solid.
The the the common pattern I'm seeing is solid's getting used a lot in React tooling where people want lightweight stuff to use around React. Um the
tanstack dev tools, tanstack router dev tools um are big examples of this. Uh I
think Aiden from Million just made another tool using using Solid. Um but
yeah so is my screen sharing working okay?
Yeah. Okay. Just double checking.
I it depend. Well react grab here. The real question is,
here. The real question is, oh, nice. I mean, React Grabs only only
oh, nice. I mean, React Grabs only only 7,000 uh downloads a week, but that's because
we're a direct dependency. That's 7,000
new downloads for solid a week.
So, props. Yeah.
gets the use of the web components in React apps that too.
But yeah, I mean for whatever reason I I think more people use Solid than they realize. And I think this is it's funny.
realize. And I think this is it's funny.
We're in this weird place where I feel like we're way less known than spelt for example even the next closest one cuz like I mean what's npm trends? How are
we doing these days? MPM trends salt js I'm pretty sure the next biggest library is spelt after us.
Yeah. Yeah. Selt's around 2 million. So
we're like half as stealth downloads.
But I feel like we're also like less than half of spelts get up stars. So
like way more people know about spelt than they know about solid. You know you see that on popularity polls and stuff like where it's like how many people have heard of us. Not many people have
heard of us. So um this is a good saturation for us like versus I think the number of people who know about solid versus the number of people who use it. That being said, we're just
use it. That being said, we're just peeons all all of us together compared to React, right? Like I I like that there's a white space below the line now.
But you know what's interesting people don't realize this pact is actually the second most downloaded framework according to this.
I think like if I know everyone knows about Angular and Vue, but was it Angular at Angular Core?
Angular core.
Yeah, look look look at the scope of things. Angular is like here. Let's do
things. Angular is like here. Let's do
what's view.
Yeah, pact past view recently.
So, let's remove react because it's just freaking depressing. and and look at the
freaking depressing. and and look at the stuff again. Like
stuff again. Like I I I mean I don't I don't even know how to explain React's growth over time recently. Maybe AI. It's just we're
recently. Maybe AI. It's just we're getting to such a weird place where like like Pact saw a spike because of the remix announcement around here, but otherwise you can see it is still on a
rather good trajectory, so to speak. Um
it's just interesting. Downloads aren't
everything obviously. It's just it's it's like this spread has pre-act at 8 million or 9 million and solid at the bottom here at uh about 1 million. So
they're about 10 times more downloaded.
But like then you go to React. This
wasn't always this way. If we go back past a year and React is like 50 so 50 times, right? See if you go back five years.
Uh let's go back farther. All time
application error. H we killed it. No.
Yeah, it literally can't handle it.
It's because of React's data. Okay,
whatever. If we if we go back in time, what you will see is that Vue and Angular used to be about about like 5 years ago about half of React's downloads. But now React has gone 10
downloads. But now React has gone 10 times big higher on downloads. As I
said, downloads aren't everything, but it's it's like all of us are like like this compared to
React. So, it's it's kind of funny.
React. So, it's it's kind of funny.
Yeah.
Salt is huge at this point.
Um, spell cro.
What happened to December 24th? A
Christmas miracle. I'm not really worried about that. But most time during Christmas break, framework downloads dip. Apparently last year we had a
dip. Apparently last year we had a spike. I guess people were interested.
spike. I guess people were interested.
Yeah. Then compare React to WordPress and it's like not even close. Yeah, I I know. It's all matter. Add lit. You want
know. It's all matter. Add lit. You want
me to add lit? Sure. Let's add lit.
Lit's actually surprisingly more used than people realize.
lit actually slides in there nicely between Angular and Felt. So, you know, props to them there. This here, by the
way, was the weird spike. Like, there's
a I don't there was a weird spike a couple years ago that happened on a bunch of frameworks. Oh, no. They they
they did they take them out? The weird
spike? Oh, no. They're back here. Okay.
No, this was must have been the spelt 5 release then. that spike there was like
release then. that spike there was like a Okay, that makes sense. Was this
around December? No, August. What?
That's an interesting spike. August
2025.
Yeah, I don't know what that spike is.
Anyways, doesn't really matter.
Um, but yeah, lit lit is nice in here around 4 mil about double spelt. A
little bit under angular core. Yeah,
it's not even the big three anymore.
Yeah, I mean it's it's like React and literally everyone else.
Yeah. Yeah.
This is the world that remix. I mean,
it's harder every day. Like I love my buddies over here. Uh, that's not it.
Over.
But like getting ground is so hard.
It's so hard.
Uh, let's remove React. It's taking up too much of space. Let's remove View.
Let's remove what's the next one? Angular.
Let's remove lit.
Remove fault. It's just it's Yeah. So freaking hard.
Yeah. So freaking hard.
Is this the right package? No. I mean,
it's still going up slightly. It's
10,000 12,000. It just
is Remix 3 something we can download yet?
Probably not.
Yeah. No, it it's it's just I mean we have to get over the training bias at some point. I'm not worried about that
some point. I'm not worried about that as much, but like it's just so propagated.
Stencil. Is stencil still stencil still a thing?
I didn't even realize stencil was still around. Stencil core.
around. Stencil core.
Okay. Stencil is still stencil. I didn't
realize stencil was that used. Props.
They took off quicker because they were react like for the virtual DOM. But it
looks like we've finally uh we finally caught up to them. Nice. The one that I I often look at is my buddies over here, Astro.
We're very close. We've always been very close to Astro. Yeah, our curve is almost identical.
It's like we just sit right on top of each other. They're great to use
each other. They're great to use together and they're Yeah, this is part of my whole theory about the time that you come out having a huge impact like the way the curves line up.
Astro and US have like almost the solid was a slow start but because no one really paid attention to us before 1.0 know, but yeah. Anyway,
I mean, we already showed lit, so Stencil's way lower because or it's Stencil's about 1 million, a little under solid, maybe like 800,000, and Lit's like 4 million.
Um, yeah, [laughter] this makes sense. I
use them together. Yeah, I mean, this must be why this graph is this is this is Jark's usage graph right here.
But no, yeah, I it's it's a good combination. Um, okay. Um,
combination. Um, okay. Um,
hackathon solid tanstack start hackathon means you can use solid. I didn't
realize this at first, but and I was like, Tanner, why don't you give me a heads up? Well, apparently the ComX guys
heads up? Well, apparently the ComX guys like just basically just went ahead with themselves and everyone jumped on. Uh,
Code Rabbit, Sentry, Cloudflare. Look,
640,000 in prizes.
Just everyone wants to be part of this.
But what's cool and I big props is full stack framework powered by Tanac router for react and solid secondass citizen makes me very excited you know show that
solid love out there. Um awesome awesome awesome awesome um very cool.
Yeah.
Just throwing that out there.
I was like stoked when we got like 15,000 or 20,000 or whatever for our last hackathon 64 I I guys.
Yeah. Did you know that tic start with Soljs I built a live hockey draft kit?
Very very cool. Did I not like this one already?
Native script. I I like highlighting when people use Solid for like stuff that's like people don't realize you can do native development with it, you know, stuff like that. Um,
conference talk.
Yeah, here we go. React grab written with solid. Turns out having to reactive
with solid. Turns out having to reactive page can cause errors, blah blah blah. A
lot of signals here, but at least there's more than one. Um,
very cool though.
Love Aiden's work. So
panstack dev tools feature as long as having v plugin even for friends of solid. Yeah, this is side note people
solid. Yeah, this is side note people the dev tools for tanstack start have the this cool like live component view that it works even for solid as well. So
very cool seeing this stuff like evolved in the space, having a framework that cares that much about DX working so like seamlessly with us for mission valot
it's so cool to be put on that list. um
you know like you know we're we're when new libraries come out they're they're thinking of us now which is a big big change before you know I there might not be the big three
but you know there's the there's React and then the the small seven you know so yeah
I don't know it's it's it's funny um but awesome okay let's keep Um, what what else do we got here? Uh, bookmarks.
Oh, yeah. It's funny. I I didn't talk about this before, but this is this is actually general software product advice. Never name a feature X mode. The
advice. Never name a feature X mode. The
word mode denotes there are multiple.
You were telling yourself that there are multiple ways that you use your product.
We must all learn. At least lie and pretend that's not the case. Don't use
that word ever. I don't know. He wasn't
probably talking about concurrent mode but it made me think of concurrent mode and it made me think about the async features. This is what earlier on the
features. This is what earlier on the when I was talking about the mode thing I was just like he is so right like the fact that the same thing like we do have
modes definitely you know like we have uh like SSR or hydration versus not you know like there are things that operate under different pretenses
um but yeah definitely um sorry Dev this is great the idea we will regret reinventing RPC yeah it's so funny a lot of the chatter
um about React's source felt remote functions and you know how amazing they are and don't get me wrong they are definitely amazing. It's just when we
definitely amazing. It's just when we basically unveiled the same thing there's there's details but Rich was not a big fan originally. He actually had a whole conference talk where he kind of
picked apart why he didn't like it. I I
understand that he addressed a lot of those with the design of felt but m you know that was never the case the thing that I was worried about. It's all about the mechanics of how the thing needs to
work. And I'm glad that he kind of we
work. And I'm glad that he kind of we got to the same place. Um, but it is sometimes interesting.
You know, we might still regret reinventing RPCs. I'm not saying there
reinventing RPCs. I'm not saying there there isn't a possibility there, but I just bookmarked this because it reminded me of that talk that was like very brutal about server functions and whatnot.
Maybe a little poor former to literally every library for your act. Yeah,
that's that's that's probably how it goes. It's nice.
goes. It's nice.
Oh, yeah. And then
Yeah, I don't even want to talk about directives.
Maybe. Did we talk about them last time?
Let's pretend we talked about them last time.
Yeah, we're going to pretend we talked about them last time. Um, just look at my other bookmarks here.
this about remix.
Oh yeah, this was an interesting interaction. You probably heard Remix 3
interaction. You probably heard Remix 3 doesn't use React. It use GX but not React. This means that you cannot use
React. This means that you cannot use any React library with it's 99.9% of them use hooks which won't run in R3. I
thought it was a huge advantage until I realized one thing, one simple trick.
It's simpler to integrate vanilla libraries R3 Remix 3 than it has ever been with React. It's funny R3 and Remix 3 is going to confuse me. We're talking
super early. Part of remix is going to improve easier. Why? Because remix feels
improve easier. Why? Because remix feels and is just JavaScript. It turns out pairing JS with JS is rather nice given how focused is on events. All you need to do is define specific events your library integrating scribe. Let's say
the team takes your new remix.
And right responses to be fair this is true of any run component body once framework especially nonb ones like sol and spell. I think view even um is
and spell. I think view even um is relatively easy to integrate because of the body component body runs thing. More
often vanilla libraries are dealing with direct DOM refs and wiring subscribers will generally fit into reactivity of the given solution. So while it's been my experience that this is true, it's nothing new outside of React and people have still generally will make rappers
albeit simpler ones simply get the component interface, right? You you want to like absorb it as a component rather than just like write a bunch of ref wiring code yourself. The real obstacle is the fact that alone doesn't stop people from targeting React
specifically. So even if all generic
specifically. So even if all generic solutions work out of the box, someone will always be able to point out some library that just came out 3 months ago that everyone has to use and only React, then you will get your own port a couple
months after, but by then everyone has moved on. Repeat. I mean, that's a very
moved on. Repeat. I mean, that's a very pessimistic view, but it has been my experience. I just thought it was
experience. I just thought it was interesting, you know, like it's the remix kind of like a community discovering something that's been like true of Sweld and Solid for years. Like
literally, you just, you know, vanilla just kind of pops right in your lap. Um
um suppose f exists but you know he's talking about like generic like tan stack it's good. Yeah, signals aren't why people like Solid more than React.
It's the component closure. I suppose
it's more obvious in Remix 3 to say this is integration part for everything though. In my ignorance, I imagine you'd
though. In my ignorance, I imagine you'd have to turn everything to a signal to get solved update when integrating. I I
love Dev's response. This is so cheeky.
You're not wrong. Solid does require turn things to signal so that update changed.
Change it to signal do something set state. let state versus single state. I
state. let state versus single state. I
I he this is such a funny thing. He's
showing that it's the calling the set state versus calling the set state and then calling update it. Like obviously
you it's one less line of code. Um this
is an oversimplification. I just think it's sorry it's funny. Um
yeah, but yeah, turning something into a signal isn't a big deal and it's not the end of the story. You should actually be
the story. You should actually be driving data. Um, but just be humorous a
driving data. Um, but just be humorous a little bit. Um, let me look here.
little bit. Um, let me look here.
Um, yeah.
Why did I bookmark my own post? I'll get
back to that.
That's funny.
Do I want to talk about this right now?
Let's leave that to later. Let's keep on going.
Yeah, we're going to skip directives.
Did I not talk about this on stream? I
guess not. Uh the post about building uh stuff in 10 different frameworks.
I guess I never talked about this. This
article is a little long and hard to read because there's a lot of repetition. The takeaways I think are
repetition. The takeaways I think are mentioned multiple. But I think this is
mentioned multiple. But I think this is basically this.
Did we not talk about this?
I don't know. Basically,
I was tapped on the shoulder to see if they were doing the right things and then I was worried about like store performance and I had no idea that they were going to do loading performance. Um
what's interesting here is that um compressed wise um I don't know why felt doesn't compress as well as we do and for some reason they're ordering it's
non-compressed. type I generally always
non-compressed. type I generally always or it's prep.
Yeah. Fell kit 54 solid 41 spell kit 47 solid 21. So we're actually if you
solid 21. So we're actually if you actually use compressed size instead of non-compressed size we're actually like probably the third.
It's basically Marco Astro Solid start in terms of code size which is interesting when you have a framework like quick but quick quick doesn't hydrate but its code size is a little bit larger but it scales better over
time so it's it makes sense why a simple demo um but I I it was interesting start performed really well in this comparison
both in code size and loading speed I think it was it was the the only takeaway I I don't know if they I thought there was more graphs. I thought
there was I thought there like there's one that maybe this that's a bundle size. I thought there was one about loading size.
Does it this article the article get gutted? Yeah, I think maybe the article
gutted? Yeah, I think maybe the article got gutted because there's one that originally had like first contentful paint and stuff.
Yeah, that's weird. I don't know what happened to it. Anyways, doesn't really matter. Um, solid solid start did very
matter. Um, solid solid start did very well. Handstack start with solid did
well. Handstack start with solid did decently well, which is interesting because you could compare it with React and see the difference. Um, not just in size, but on loading time, but it looks like all the loading times are gone. So,
in either case, I think the the only loser here was Nex.js, JS which yeah I mean
I don't know it's not too surprising not much to say on this one. Um
I it just yeah I don't know. This has been one of those things where it's like you're looking for the right kind of comparison and you just I'm just not finding it's it's challenging now to really
differentiate a lot of the solutions.
Even if there's like a performance difference, you're kind of like, yeah, I don't know.
Joe did this. Yeah,
this is the only Okay. Yeah. A thing you can do when seeing a techno choice you disagree with, pause, take a breath, be curious, ask yourself why they might have done that, and ask the officers why they did that. In what context did they
do they don't? Yeah. This has been really helpful for me. I spent you this whole stream was me analyzing why reactants felt did what they did. Um,
and this has been very helpful for me. I
do this a lot and I really have to thank my time with Marco for influencing that because Marco originally made me really challenge my assumptions. I have to like put on a different hat when designing
for Marco and designing for Solid. So,
um, yeah, I think it's important that we always kind of step back and think about like the reason why different decisions than what you think you'd make would happen. We talked about React APIs
happen. We talked about React APIs today, like why do they look the way they do? They they start making a lot
they do? They they start making a lot more sense when you think of async, you know, stuff like that.
It should be archived on your YouTube.
Yeah.
He said there's a bug in the benchmark.
Okay. Well, whatever.
We already talked about the hackathon.
Do we want to talk about isomeorphic first? We can talk about briefly. We
first? We can talk about briefly. We
talked about this last stream, so I'm not I don't need to go over to the article very much, but there's a a different approach to server components that I think is kind of interesting that
I kind of designed out and Tanner's going ahead and implementing. Um I to help people understand why it's important. I think this is an approach
important. I think this is an approach to server components that actually doesn't make things far on the right here like apps actually feel the pain.
It's the way I think I would approach it if I you know was trying to add it. I do
see a huge benefit to this approach like the typical RSC approach. It just I understand why there's friction on this side of the graph and I I really was trying my hardest to figure out if I
could extend this the whole way and I just didn't ever succeed. And I think this mechanism that I kind of came up with, well, it doesn't it's not as nice over here. I think I think it's
over here. I think I think it's interesting and it'll be interesting to see how it works in Tanstack, but as someone kind of thinking spa first, spa
SSR, it's the next kind of natural step here. So, um,
here. So, um, we talked about last stream, so I don't need to get into it again. If you guys if you're interested, check out my segment with Tanner right before um the
the the um microrend stuff. I think the more interesting thing here is that is that we're kind of building this idea of like a almost client first but
isomeorphic first framework. I've talked
about this on my previous like uh like where frameworks are heading streams every year where I was talking about this big divide between like stuff that's working like server components or islands and things that are spa first
like you know typical SSR like spell kit tanexard or even quick like there is a split architecturally on the mentality and I feel like um funnily enough react server
components and islands are on one side and like ourselves tanstack felt kid um and quick are kind of like on the other side and ducks also on this side. Um so
it's interesting to see how both sides kind of develop because I think they're both viable approaches but this was like kind of making a manifesto almost. Um
which we talked about last stream dev tools. Cool.
dev tools. Cool.
This one cracks me up. Let's talk about this for a minute.
The HTMX guy was like there will never be another version. basically he's like there will no be but never they're apparently releasing a new version of HTMX and I'm like I thought it was so
like I I got why they he thought that he wouldn't need to and he was trying to build stuff into the browser but I I said this at the time you can probably go f back and find when it when they when he released the
whole thing where was like there won't be another version of HTMX blah blah blah I'm like I said that was the cockiest position one could basically take ever that's like that's like saying that I did it the first time and I 100%
got it right. Like that is like you you have to have some balls to to come out and and say that and and you know maybe he does. Um
he does. Um but like uh we're all just human after all. You
know like I I I think I think change is a constant. So this is this shouldn't
a constant. So this is this shouldn't have been a surprise to anyone. Um, you
know, part of me was like we shouldn't revel in stagnation, but this is real realistic here. He's not saying he's not
realistic here. He's not saying he's not saying stagnation like he what something came about that was worth changing and worth updating and they're going to do it. So, it's not about stagnation, but
it. So, it's not about stagnation, but it's it's about, you know, humility, I think. Um, that's only my perspective
think. Um, that's only my perspective obviously, you know.
Yeah.
Yeah. I mean, that's perfectly fine, right? I'm not saying it doesn't matter
right? I'm not saying it doesn't matter what it is. It's it's the fact that it could be the most trivial thing. It's
just there will be more changes. Yeah, there
will.
Yeah. Uh, if you didn't watch previous stream, there's a whole road map for Solid Start V2 built on Solid 2.0.
They're already in progress. We're kind
of building these things out in parallel. Um, there's an update to Solid
parallel. Um, there's an update to Solid Start pre.0 that's getting set up
Start pre.0 that's getting set up getting off and then the final bit will be with 2.0. So like there's already a plan in place in a road map. If you go to the solid start repo, you will find
it. And actually's
it. And actually's talk at beat comp this year was the plan for this. actually he did a whole
for this. actually he did a whole conference talk on it.
Of course, it doesn't matter the it's not it doesn't matter what the change is. It's the fact if you just were like
is. It's the fact if you just were like I'm never doing another version that is so like like I I don't I don't care who you are, you know? It it doesn't matter
like or never like another major version. That's that's like it it
version. That's that's like it it doesn't matter what the change is.
That's just like the peak of of so a bunch of breaking changes. Yeah. I
mean things get better like things change. It's just it's it's tricky when you're positioning is like web's too complicated. Things should
never change. And then you're like, then you're like, but I do need to change myself, you know, it's like, yeah, I I love this meme. Sorry, just moving
on. The use effect problem.
on. The use effect problem.
It's just because people are wired this way. I think everyone's seen this meme
way. I think everyone's seen this meme by this point.
you know, the fact that like everything fits in the square hole.
Whose whose fault is that? You can say it's a design flaw in this unit. And I I could agree with that, but sometimes
like the square is the right is the like the right shape hole and we need to realize that there's more specific solutions that fall fit better.
Uh anyway, um it was just there's so many people. It's
funny on again on Blue Sky people like, "Haha, that's a funny meme." On here it was just like tons of people just being like, "React is the worst. React is the
worst. React is the worst. React is the
worst. React is the worst. React is the worst."
worst." Or people being like, "It works. Why
should anyone be bothered?" That's
scary, too. Um
uh uh but yeah, I mean it's fine. Let's
keep on going. Um we talked about use effect.
I want to show this this quick little clip here.
We talked about RxJS a bit at the beginning.
This I look at this answer originally.
It was from July 22nd, 2011. This was so fundamental to my understanding of why KnockoutJS was great. And
um I just I'm going to love this. I
found it again. I figured I was looking around and this was like what makes Knockout or Signal special. They called
them observables because which is confusing because RX also had observables and then there's this whole that's why they got thing. But he this explanation perfectly explains to me why
like signals exist and why stuff like solid exists versus just using RxJS. Um
I'm very familiar with Rx JavaScript having recently used it heavily a big project and aspects of the design and knockout are made with my RX experience in mind. The key difference between
in mind. The key difference between knockout implementation of observer pattern RX is that knockout automatically infers the association and dependencies between observables from regular procedural code without you having to specify them up front through special functional API. I want Knockout
to use regular procedural imperative style code as it's more familiar and approachable for most developers.
Another difference is RX is optimized for composing streams of events without state. At first, I was enthusiastic
state. At first, I was enthusiastic about this and its functional purity, but after time it felt increasingly like I was jumping through awkward hoops and had to invent extra ways to simulate state to manage UI commands sufficiently. That's why in knockout all
sufficiently. That's why in knockout all of variables can be treated as stateful.
For example, you can always read their latest value, which is cache by the way.
It doesn't recmp compute until the underlying data changes. RX goes further in Knockout to advanced ways of composing event streams, whereas Knockout goes further than RX into UI development, letting you bind its observables to HTML DOM events and
templates and manipulate them any way you want. RX is great at what it does,
you want. RX is great at what it does, but turned out not exactly what I wanted to build rich UIs, hence I designed Knockout.
This is great.
Um, I don't know. Just
I feel like when I read that that it was like the same thing, you know, like years later. I I think I I had to read
years later. I I think I I had to read that multiple times. I knew it at the beginning and then I think when I tried to build solid at one point on top of RX observables and just expand them because they were going to be built into the
browser as one of those guys who thought like standards, you know, trying to do standards stuff back then about 10 years ago. I think I had to read that again to
ago. I think I had to read that again to remind myself why like they weren't the best fit. Um is it's it's funny you know
best fit. Um is it's it's funny you know this but this is the first suggestion like how it's a very unique or distinct thing. It took some time for us to
thing. It took some time for us to change the name from observables so like RX could keep them and then we could view signals but essentially it's the same kind of mentality behind it. Talked
about API designs, we talked about all this. Have we caught up?
this. Have we caught up?
Yeah, we have caught up. Um, yeah.
No, but browser events aren't stable or is this a remix job, isn't it? Of
course. Too slow sometimes.
It would have been awesome the square cube went in a different hole, but uh Okay. Um do I have anything else? I
Okay. Um do I have anything else? I
think the I think I'm fine with this now. I
now. I there was just this like brief moment in time where I where I I saw a bunch of I I only flagged this one but I saw a bunch of versell related like I think
Karma was one in Malta was another one both being like wanting to remind people that they funded initial like Tanner on his initial work when he started tanstack starter tanstack router he he
wasn't really using start I think as much time he was doing router mostly at the time, but at the beginning of it, like about a year and a bit ago, um Versel actually was the was a major
partner. It was just I think there was
partner. It was just I think there was an interesting tension here on the politics. Um because I was like,
politics. Um because I was like, yeah, was it was it the view post engagement?
Let's see what this is.
National phenomenum. See all these people are like is Verscell gonna buy Tanstack and it's like no since felt now start it's not now this was like two years ago people have no clue
but then there's like this whole political angle this kind of feels like a friend who lends you money when you're down on your luck then brings it up every chance he gets I've seen we're funed hand tech a million times but I
don't see other people who have done the same say this at every chance they get odd that's fair that's that is that's why I bookmarked it there just something that it felt really weird around that time
period. Um, but
period. Um, but whatever. I can't be bothered to care
whatever. I can't be bothered to care too much on the other end. In either
case um maybe it's best that we all kind of stay away from the political aspects. Um,
oh yeah, yeah, Tanstack.
Yeah, for sale share is 5%. So compared
to what others have contributed since it's it's it's it's not that big but like yeah you could see by the the by the like versel fans like reactions to
this where people are like bend the knee like people are treating this like and I I I kind of I saw Tanner actually um
um at US thing. It's like it's not like we people aren't appreciative of the funding and stuff and it makes a huge difference like super appreciative. It
was just um you know sometimes like the because of some of the like hype kind of thing around this. I remember when Verscell first uh funded solid $100 a month which is pretty small compared to
pretty much everyone else they've ever funded but you know they they gave us a small amount which for us at the time was actually a decent chunk for us. you
know, it wasn't, you know, they and they paid that they gave us that for over two years. So, it was it was really thing.
years. So, it was it was really thing.
But I remember like the theing gauntlets like the like Thanos gauntlets like you know Versel and Giamo on this and like the hype thing and it was just so funny cuz the announcement looked better than
the like the reality of the of of the stuff, right? like um it was just it's
stuff, right? like um it was just it's just kind of weird how like the the these narratives start kind of popping
up, you know, like um Yeah. I don't
know. It's funny.
Yeah. Yeah. It's Yeah, it's funny. I
I've I've seen that. I've seen this before and yeah, TANSAC is very widely um you know there's sponsors. I I need to do a better job
sponsors. I I need to do a better job getting sponsors for Solid. I I it's funny every time I talk to people I'm like you guys you guys realize like if we actually had developers the future would be here that much
sooner. The biggest contributors we've
sooner. The biggest contributors we've had over time obviously Netlefi and Sentry paying wages but also from the outside perspective um Google the money
they put into uh that they gave us from the whole uh that program they they did um that went to the research into our server components and into like which
basically created Saravel which is the serializer behind solid and solid start and tan stack start and I think they're even using an Astro now I double check that was actually originally from Google
funding getting an Alexis on there. So
like these things make a difference.
It's just so funny like the server component thing that Tanner's talking about, you know, I' I've been sitting on that for like a year, you know, like and I had other approaches like there
there's I just didn't have the means to finish it at the time in Solid because I had to do other work in Solid. And I
feel like um there's so many things that if we had the same kind of funding or capability, we'd be able to do like it's not from a lack of direction or ideas or
you know a whole bunch of other things.
It just it's just where we are anyway.
Anyway, uh I think we're done. Uh it was a long stream today, six and a half hours, but uh I'm not going to be streaming for the next couple weeks. There's like
Thanksgiving and then some I think my daughter's first uh ballet uh performance for her Nutcracker this year. So, um I'm going to be away for a
year. So, um I'm going to be away for a little bit, but uh I'm glad I got to show what I'm working on. I'm sure by the next time I'll have this all impmented in the JSX and we'll have some really sweet demos to show. But I think
I think it's a really interesting direction to async that we've been taking. And I think I think when you see
taking. And I think I think when you see the pieces together, I think it's I I finally feel like it's it's jelling.
Like there's a lot of tra there's more trade-offs than I would have wanted at the onset, but the end result is much cleaner. And I think this is going to be
cleaner. And I think this is going to be really really powerful moving forward.
All right. Anyways, you all have a great one. Till next time.
one. Till next time.
Loading video analysis...