Invisible hand of React performance with Ivan Akulov === Noel: [00:00:00] Hello and welcome to PodRocket, a web development podcast brought to you by log rocket. LogRocket provides AI for a session replay and analytics, which surface the UX and technical issues impacting user experiences. Start understanding where your users are struggling by trying it for free at log rocket. com. I'm Noel. And today we have Ivan Akhlov joining us. He's here to talk about a talk. ~Uh, ~he recently gave the invisible hand of react performance. How's it going, Ivan? Ivan: ~Um, I'm ~it's going well. It's raining in Amsterdam as usual. ~We've had three days of summer and now it's, now it's raining again. ~How's it going for you? Noel: it's good. It's raining here as well. I like it though. ~It's like, ~it's like a break from the heat for us. ~It's like, it's been, it's been hot, so I'll take the~ Ivan: ~Oh yeah.~ Noel: ~the rain for a day. Yeah. Yeah. Um, But ~Cool. Let's jump right in. ~Uh, ~react performance. ~Why, uh, I guess give us, ~Give us an overview of like your history and why react performance is something you've been focusing on and ~kind of ~how it's evolved over time. Ivan: ~Oh yeah, ~so right now ~I'm, uh, performance, I'm, ~I'm running performance at framer, I'm a performance engineer, but before that I used to be an independent performance consultant and,~ um,~ I focused specifically React was one of my, ~uh, ~favorite things to optimize. ~Uh, I've done, ~I've done consulting for six years. I've worked with,~ uh, ~Google, some [00:01:00] other fortune 500 companies and also tone of startups.~ Um, ~and ~yeah, most of these companies, ~most of these teams that I've worked with, they're using React. So I did learn quite a lot about performance. Noel: ~Gotcha. Nice. So how, how has, um, ~As you've been focusing on react, what are some of the major changes that have happened that have ~kind of ~impacted how, ~you know, ~companies are typically writing react and how performant that code tends to be ~kind of ~out of the box ~when you, ~when you come into these organizations that have large react code bases. Ivan: ~Oh, shoot. You're, you're talking about large organizations with a large React code basis. And I'm thinking immediately over the EU state too, uh, which, uh, yeah, like I have not, I have not personally had to deal with that, but, um, I've seen, I've seen, sorry, I've just seen some recent screenshots on Twitter.~ ~It was. Um, it was a lot, but, uh, no, uh, like I personally, I think I personally,~ I think I personally started using direct around version like 0. 14, and I think most of the changes,~ uh, ~that tracked went through scenes were actually focused on architecture, not on performance. Like, I don't know, hooks,~ uh, ~that replaced life cycle methods that they made writing out of code much easier. ~Um, ~several components that are going to become stable with direct 19. They're. Going to allow to move a ton of client side logic to the server. And ~well, ~that is actually quite a performance focused release, but architecture wise, I think it's pretty good. It's like a huge win for data [00:02:00] fetching and,~ uh, ~that stuff, but ~something, ~something I loved about a lot of these changes and why my talk was titled invisible hand to write performance, that a lot of these changes, they also under the hood without me, without other developers really noticing ~that they.~ ~Improved performance of the apps in, yeah, they, they improved performance of the apps. Like, um, I know, should I, should, ~do you want me to give some examples? Noel: Yeah, ~sure. ~Sure. Maybe like some of the,~ uh, you know, like ~big changes that you're aware of. ~Yeah. ~That,~ uh, improved, just ~improved performance without any work from the devs. Ivan: Oh yeah. ~Um, ~like for example, I think one of the ~least known, ~least noticeable changes was when Rect switched ~from hooks, sorry, ~from lifecycle methods to hooks in 16. 8. And something that happened at that time was Rect introduced useEffect. Right.~ Right. Right. ~And we don't really love useEffect. Like ~nobody, ~nobody loves writing useEffect. ~Um,~ useEffect gets quite a lot of burn, but useEffect was actually pretty cool. It ~was definitely, useEffect ~had two cool things about it. ~Um, ~first it was better than componentDidMount and componentWillUnmount and all the previous lifecycle methods, because it allowed grouping all the logic in ~the ~single place. [00:03:00] And the second one was that useEffect compared to the previous lifecycle methods completely invisibly for everyone else. ~It moved, um, it changed how, sorry, um, ~it changed how ~the JavaScripts, um, change ~the scheduling of the JavaScript that it runs to solve a whole class of performance issues called layout threshing.~ Um, or~ do I know any other examples? ~Um, yeah, or ~yeah, ~or~ something that happens like very early, very early in React. Like I think it was ~like ~React 0. 3 or React 0. 4, React introduced update batching, which is when you call setStates several times in a row, React only renders stuff once. ~And ~this is also like completely invisible. I think ~like ~most of the senior engineers I've talked to, ~they ~know about this, but most ~of the ~people ~like ~just starting in React, ~they ~have no idea about this. And this also ~like ~happens completely invisibly and ~avoids ~Avoids performance issues that otherwise you would have to program around. Noel: ~Yeah. Let's, ~let's dig into that a little bit. So like,~ why, ~why does,~ um, ~use effect ~and,~ or even like component did mount. ~Let's, ~let's talk a little bit about ~like ~the rendering pipeline and how react is interacting with ~Like what are, ~what are, what are the [00:04:00] relationships of these two methods of updating state with the rendering pipeline itself ~Mm~ Ivan: ~All right. Yeah, that's a great question.~ So,~ um, ~I think to understand the difference, understand this,~ like,~ invisible change that happened between Comp and EditMount and UseAffect. ~Um, ~It helps to know a bit ~how browsers do stuff under the hood, how browsers, ~how browsers render stuff under the hood. ~So, ~whenever a browser needs to handle any update, like maybe you clicked a button, maybe,~ uh, ~some timer fired, maybe some network request came in, the browser generally goes through four steps. JavaScript, Styles, Layout, and Render. Wait, four steps. So in the first step, the browser runs JavaScript,~ uh, ~that it needs to run. Like maybe you click something, there's the event handler kicking in, right? Maybe a timer fired and there's the timer handler also kicking in. Then if any of the JavaScript. changes anything on the page, then the browser will be forced to run the next two steps, the style step and the layout step. The browser would look at how the JavaScript changed the page. The browser would update, recalculate the styles for the page. And then if any of the styles also changed the layout, like maybe some div got [00:05:00] larger, then the browser would also recalculate the layout of the page. And then finally, once it's done, the browser will paint the results. On the screen. ~this is the standard. Yeah, ~this is the standard pipeline that the browser goes through ~like ~on any update. ~And, but the thing about this pipeline is if you do something unorthodox, if you do something literal unorthodox, the browser may be forced to change how the steps were, sorry, thinking how to say, how to phrase this, um, the, but ~the challenge about this pipeline is that because JavaScript can do pretty much anything, it can also~ kind of ~break the order in which this pipeline executes. And when that happens, the whole execution could get slower. ~So like one of the common cases that you might, you might have heard this name, ~one of the common cases when this happens, one of the common anti patterns is called layout threshing. This happens when inside JavaScript, You change something about the page, like change some styles,~ change, ~change some layout, and then ~you try like ~within the same JavaScript logs, like within the very first step of this browser pipeline, you try to read the updated styles, something from the page again, read the updated styles, read the updated layout. So normally the browser would recalculate the layouts at the second and ~at the ~third step. But ~if you try, ~if you update the styles and the layouts in JavaScript and then try [00:06:00] to read them again in JavaScript, the browser will be forced to pause your JavaScript execution, force recalculate the styles, Also calculate the layout and then continue executing still in this first step, still continue executing whatever it is running. And if you do this multiple times in a row, then the browser would be forced to do this multiple times in a row,~ um,~ over and over making the whole,~ uh, ~JavaScript step much, much, much slower. So this, when this happens, this is called, ~this thing is called~ layout threshing and indirect world before use effort. This was a pretty. ~ Like, ~let me give you an example. ~And I'm going, I'm going to come up with this example, like right over the top of my head. So maybe it wouldn't, wouldn't be amazing. Um, but like, okay, so a really dumb example, let's say, ~let's say you have ~like ~a page with 50 buttons, right? And every button ~on, ~on render, it tries to figure out if the text inside the button is longer than the button itself. And if the text inside the button is longer than the button itself, then it would render something. I don't know, fade out effect over the text, ~so like to, to, ~to show that the text overflows. ~So if I was, um, if if this was all implemented with component indeed mount, or~ if I was implementing this with component indeed mount, the way I would probably implement this is,~ um, ~for every button [00:07:00] component, I would add a component indeed mount lifecycle hook, which would read the length of the text. And then change the DOM right inside CompanyDidMount. ~Um,~ it would change the DOM,~ it would apply, ~it would apply this fade out effect maybe through setState, maybe by manipulating the DOM directly. ~Um, ~but ~what, ~what would happen then is, when 50 buttons render, Rack would execute that CompanyDidMount 50 times in a row, and every time in a row I would try to read from DOM, And then write it to dumb again, and then read from dumb and then write it to dumb again, then read from dumb and then write it to dumb again. And that would force the style layout steps ~happen ~to happen ~over and ~over and over again.~ Um, so maybe I'm going too much into trenches here. Um,~ Noel: ~It's good. Continue. Yeah.~ Ivan: ~yeah.~ Noel: ~hmm.~ Ivan: ~So this is, this is what would happen with, with company mount. What's actual, sorry, sorry, a bit more details. Um, ~so this is what would happen with company did mount. And the biggest challenge here, I would say is that ~the first time Um, so every, ~each of these did mounts, it would force the browser to recalculate the styles and calculate the layouts. So to run steps two and three of the browser pipeline earlier, but the biggest challenge is that the very first company did mount, it would kick in right after React [00:08:00] has finished a big update, right after React has rendered this 50 buttons, like we've just mounted the 50 buttons, right? ~so the first time the. Um, the first time react, ~the first time my company did mount tries to read from dumb that style recalculation, that layout recalculation, the steps like the forced steps to enforce step three of the pipeline,~ um, ~they would be really, really expensive because the browser would have to recompute a lot because ~a lot, ~a lot of information on the page has changed. ~So what use Epic does. ~So when useEffect was introduced, the idea behind useEffect was that you would put the logic that you had in your companyDidMount, companyDidUpdate, you would put all that into useEffect. And it would ~kind of ~be like architecturally organized, the better, et cetera. But something that useEffect has done under the hood was it silently moved whatever logic you put into it from this first step of the browser pipeline from the JavaScript execution to the next frame, ~to the frame, so to, ~to the idle time after the whole ~browser handling pipeline completes, ~browser rendering pipeline completes. So what would happen with useEffect now is, [00:09:00] again, remember there are four steps, right? JavaScript style layout paint. If you render these 50 buttons, but now if my logic lives inside You said it would happen now is first the browser would run the JavaScript to it's rendered this 50 buttons and That would be it. There's no company did mount now so no Logic that's like measures the buttons. It's the fadeouts. Nothing like nothing. Nothing of that happens just yet Then the browser would run the style step and the layout step it would recompute how the 50 buttons affected the layout of the page and then it would paint the result on the screen display into 50 buttons ~You ~And then, only after all that is done, the useEffects will kick in, and now, when useEffects kick in, what they would read whenever they go and read The DOM, what they would read is they would read the styles and the layout that were already recalculated during the previous frame, because ~the browser, ~the browser completed its rendering pipeline. It had a [00:10:00] chance to render the 50 buttons in the natural order, JavaScript style layout planes. ~Um, ~the useEffect that happened after all that, and specifically the first use effect, the next two effects, like if they, If some of these useEffects update the DOM, the browser would be forced to,~ like,~ do some recalculations again. But the first useEffect Previously, ~when it, ~when it was a componentDidMounted, it was a really expensive forced recalculation because during the JavaScript step, we tried to read something, we first updated the dumb rect, updated the dumb, and then we tried to read something from the dumb. And now all of this happens after the whole browser pipeline completed in the natural order. So ~the, ~the browser would just. Read the cached value of the layout and no, no forced recalculation would happen. ~I, I don't, ~I don't really know how clear was that because I normally explain this with pictures.~ So, um, uh,~ Noel: Yeah, you're good. I think I tracked. I think I'm with you.~ Um, yeah, I don't, that. I don't think that was overly tricky to follow.~ Why could we not do this like in the previous era? ~Why, why were, you know, ~like component didn't mount as an example? Why could it not function this way? What about the architecture that will [00:11:00] allow us hooks to ~kind of ~leverage this specifically? Ivan: ~Oh, um, sure. This is a good question. So think the reason we didn't do this. So. Um, ~there are like ~two, ~two ways to answer the question. First, why React didn't do this. And second, why we app developers ~like ~weren't able to do this. ~So we, ~we have developers. ~We can, we,~ we could have totally done this. ~Like you could, ~whatever code you have inside your company did mount. You could wrap it with like set timeout zero. And it would also move it to the next frame, like after the whole. Browser thing completes, but I think the reason react ~and I'm speculating here, but I think the reason drag ~didn't do this was that they simply ~didn't know, ~didn't realize that would be a challenge. ~Like, um, it, the, at the moment when rack was introduced, what, what, ~what I'm going to speculate, like my understanding is at the moment react was introduced the life cycle methods design seemed like the most natural design.~ Um, like ~sure you want to run some logic when the component mounts. Sure. Here's the componentDidMount with that logic there. And only after ~like ~a lot of developers adopted company did mount and ~like all ~the lifecycle methods and only after rect was able to actually see how all the stuff is used in the wild and what issues it causes, then they were able to.~ Um, ~look at all that and [00:12:00] understand that, okay, ~this, like ~this pattern causes,~ uh, ~layout threshing or that pattern causes something else. ~Um, ~they weren't able to change specifically how component did mount is scheduled because ~that was a, ~that would have been a breaking change that would break some code out there. So they had to do this while introducing the new API specifically hooks. Noel: So ~do you think, ~do you think that this was like a convenient by product of the API changing entirely? Cause it feels like, again, ~I don't, ~I don't know that react internals that well, but it feels like ~we could have used some, some like, ~There could have been some component did mount that implicitly also ~like did it, you know, ~popped the rendering to the end of the event loop as well. ~Um, ~but like we're rewriting everything. ~We're telling it, ~we're telling app developers to write everything using hooks anyway. Now is like a reasonable time to rip off that band aid ~or is there, ~or is there anything structural that actually Ivan: No, I think, Noel: world? ~Yeah,~ Ivan: ~yeah, no, I'm, um, like ~what I know is that ~like ~in version 15, React moved to the architecture, but ~I, ~I don't honestly know ~if that was related to, if that, ~if that was what allowed hooks,~ um,~ I don't think it was because I know the fiber architecture was mostly introduced for concurrency, which [00:13:00] came out in React 18, which ~is, um, ~I think we'll talk about that later. It's, Honestly, my favorite subject, my favorite change also ~in, ~in, in rectitine. ~Um, ~but yeah, my understanding is it was simply ~like a ton of, ~a ton of changes meshed together because it was a convenient moment ~to, ~to ship them all. Noel: Yeah, makes sense. ~Cool. I mean, let's, ~let's talk about some of these other,~ uh, react ~react 18 changes while we're here. ~What, what were the big. ~What were the big changes here? ~Um, you know, that, ~that majorly impacted performance. ~Hmm. Hmm.~ Ivan: ~So I think my favorite change of rectitine is, hold on, no. ~So rectitine has ~two generally big,~ two big performance changes generally.~ Um, ~the first one is recti concurrency. Which is, it's a fancy name for a very simple idea. You can take your render, which can take 500 milliseconds,~ uh, ~and which can block the page for 500 milliseconds while it's happening and you slice it into chunks of five milliseconds. So that the page is not blocked for 500 milliseconds anymore. It took React like five years to get at that point, but React has finally shipped this in React 18. And honestly, it's my favorite change. It unlocks so many optimizations that weren't possible [00:14:00] before. ~Um,~ the second change that shipped in React 18 ~was the second performance change~ was,~ um,~ better batching. ~And this is. Also ~this kind of an invisible change that I think flew like for me talking to other developers. I think it flew over the head of~ like, ~not everyone, but the majority of developers.~ Um, I know. Should we talk about batching first? Should we talk about Suspense first or concurrency?~ Noel: ~Let's talk about, let's talk about batching first while we're here.~ Ivan: ~Yeah. So, um, sorry. Thinking, thinking how to start. ~the gist about batching changes in Rect18 is basically as follows. So I mentioned a little earlier, we talked a little earlier about how ~Rack 0. 4 or 0. 3 introduced the very basic, uh, ~Rack 3 introduced one of the first invisible performance optimizations, which was batching. But the thing about the batching was that the batching was not really working perfectly. ~Like ~there's this question that I really like asking,~ uh, ~when I'm ~doing, ~giving talks or doing workshops, which is ~like, imagine you have. ~Imagine you have some event listener, like on click, for example, ~and you have~ inside that event listener, you have setState one, setState two, then some await, some promise, then setState three and setState four. And when I say setState one, setState two, I mean, just like some random [00:15:00] setState calls, ~like maybe. ~It doesn't matter if it's like a class component with setState or just the function that got returned by useState. But basically, you change the state once, you change the state two, you await on some premise, you change the state third time, you change the state fourth time. ~Uh, ~the question here that I love asking is,~ um, how do you think,~ how many renders would happen in this situation? Noel: Ooh, ~my, ~my gut would be like, ~you know, you, ~you want it to be like two, but I assume it's actually four or five. ~Yeah.~ Ivan: So the right answer here actually depends on the React version, but your answer about two, it was correct, but it was only correct for React 18. Noel: Gotcha. Ivan: ~So with React 17, for all the,~ with all the versions of React, ~uh,~ like starting from 0. 4 to ~0. 5, Uh,~ 17, the number of the renders in this situation would be three. And the reason for this was that this batching,~ uh, ~that we talked about, it would only batch updates that happen directly inside event listeners. Noel: Yeah, sure. Ivan: ~So, ~yeah, when you have an onClick and then inside an onClick, you await on some promise. For the [00:16:00] browser, for React, you exit the event listener, like whatever executes after your wait on the promise, it's already out of the event listener callback. It's already like just some random code that runs on some random point. So the first two setState calls, they would be batched and they would result in one render. But then ~each subsequent, um, render, it would cause sorry, it, ~each subsequent setState call, it would cause a separate render. Noel: Yeah. ~Gotcha.~ Ivan: ~so,~ Noel: ~Why was this change like done in 18? What, what were they able to do here? Um, To fix this~ Ivan: ~oh yeah, sorry. I was thinking, uh, should I, I was, I was sorry, I was kind of sitting. I was thinking, should I explain, I'm, I was thinking if I can like explain briefly, like the cool thing the track does under the hood to batch, to batch this state updates. Should I~ Noel: ~yeah. Please, yeah. Yeah, how does that work?~ Ivan: ~Yeah. Um, so, ~and the way this batching works in React 18 is actually pretty cool. So I'm not going to go that into the details, but ~I just, ~I just love how React has done this. So,~ um, ~starting with the React ~18, the way starting with the React 18, the way. Starting there, starting with the rect ~18, whenever you call a setState. Function, does not schedule any update right away. Instead, it pushes the update into the queue and calls a method, which I think, off the top of my mind, I think this is promise. resolve. then. And inside this then callback, ~it pulls it, it takes,~ it puts a function that takes that update queue and it processes that update queue. So when you have several setState calls, what happens ~in ~is you call the first setState call that sets the setState update. It's pushed [00:17:00] into a queue and a queue is just like an array of,~ uh, like ~update metadata, whatever, React just describes what should happen in it. And it puts it into array. It does not process that right away. Then you call another setState and it puts that also into the array and it calls another setState and puts that also into an array. ~And what happens then ~you can do like any other work can run any other JavaScript. But what happens then? ~The reason React,~ the reason there's this promise resolved, then incantation inside React. Is this incantation allows react to take any chunk of work, any chunk of JavaScript and put it right at the end of all the JavaScript execution. So you remember, we talked about JavaScript style, layout, paint,~ uh, ~literally order doing promise. resolve. then would take any code you put into it and it would move it right into the end of the JavaScript step, right into the end of the first step. ~Um, ~so basically ~the way, ~the way React does batching is. It's pretty magical in React 18, which is ~it relies on, ~it relies on the browsers to synchronize things around. But the idea is [00:18:00] basically none of the setState calls are handled right away. Instead React instructs the browser to,~ um,~ take the function that would process the update and execute it after all other JavaScript has been completed. And so you can call setState as many times as you want in your JavaScript. Only after all that completes React actually process the update. I think that's ~ ~really, really cool. Noel: ~yeah. ~So effectively then ~is it, ~is it pushing all of ~these, ~these updates to ~kind of ~like the end of the event loop at the same time? ~Like, like ~create a big list that, okay, well, ~once,~ once there's no other JavaScript that has been queued prior to this being executed, then we'll go through and flush it. ~Like, is that how it knows? Is that, ~is that the delineator? Is that how it knows when it's okay to finally ~like ~process a batch? Ivan: ~um, kind of. So, um, are you familiar and I don't know if we should like go into this deeper, but ~are you familiar ~between like, ~what's the difference between tasks and micro tasks? Noel: Mm hmm. Yeah. ~Gotcha. Gotcha. Gotcha.~ Ivan: ~Oh yeah, so, um, the, ~basically what's happening is exactly what you're describing, but it's only happening at ~kind of like ~the local level. So you could have other JavaScript scheduled, like you could have other timers, other event listeners, but all this stuff is going to be scheduled as local. ~Um, ~tasks, so it would run in the next frame basically in simple words, but ~what ~what drag does is [00:19:00] schedules a microtask and the microtask runs once all other microtasks in the current frame complete. And so, yeah, it basically takes its function, puts it into an event, the end, not of the global event loop, but ~kind of ~the local event loop with all the microtasks. Noel: ~Yeah, like that, that execution context of that given, um, yeah, that makes sense to me. How does this, how does this relate, if at all to, um, concurrency? Like, is, is that, is that at play here? Or is that, was that just a separate, a separate performance improvement that happened to also be in version 18?~ Ivan: ~Yeah, I, I haven't heard of any connection, so I'm thinking separate, um, yeah, concurrence on its own concurrence on its own was my, uh, sorry, uh,~ Noel: ~fine.~ Ivan: ~Could you, could you, could you give me, could you give me some nudge towards concurrency? Because I feel that nudge that just happened was not, uh, like, I, I think I'm a little worried about speaking too much. I know I'm a podcast, but I think I'm, I'm a little worried about speaking too much and not giving you enough voice because the most interesting podcast is when two people talk one~ Noel: ~no, no. You're fine.~ Ivan: ~it's just like a lecture.~ Noel: ~You're fine. You're fine. Yeah. ~Okay. So ~that, ~that makes sense to me for batching. ~Uh, ~let's talk a little bit about concurrency as well. ~What, like~ what changed here in React 18,~ uh, ~that allowed, ~you know, ~more optimized performance in these kinds of in parallel computation. Ivan: ~Oh, yeah. Um, ~So concurrency, as we talked, is a fancy word for a really simple idea. You take an expensive render and you slice that render into chunks of ~like ~five millisecond work so that the rendering does not block the main thread anymore.~ Um, it's a very, ~it's a very simple idea. ~It took React like five years, I think, to get there from 20, um, or should I misremembering when was the first demo?~ ~Hold on. I actually have a number for this. Wait, I actually have a number for this in my talk.~ Noel: ~check. Yeah.~ Ivan: ~there's like the number of days. Give me a second. Oh no. Wait, that I'm looking, I'm taking the wrong talk. All right. So, uh, apparently it took React 1489 days. So, uh, ~apparently it took React 1489 days. So, uh, apparently it took React 1489 days. To ship concurrency. This was the time between the first review of what was called time slicing in the past and the general rectaging release. Noel: Gotcha. Ivan: finally made time slicing, [00:20:00] which got later a name to concurrency. ~Um, ~which finally made it available to everyone. ~So it took, it took a ton of work. ~It was even not really well received by other frameworks. Like Vue told straight away that they're not going to implement this. Preact told straight away that they're not going to implement this. ~Uh, ~because they think it's rather harmful than beneficial. But from my experience of working with React and from my also smaller experience of working with other frameworks. What I found so far is that concurrency is really beneficial. ~So the idea behind concurrency and now I'm thinking how to phrase this. Okay. No, let me actually ask you the question. Um, ~So what do you know about concurrency? Like what does concurrency mean for you in your head? Noel: ~Ooh, I guess I'm not sure. Like, uh, in, ~in regards to rendering, I would assume it would be like, there's. ~Um, ~as little blocking action as possible when multiple components are waiting for things that need to be happening to execute and figure out what work needs to be done to render that component correctly. Ivan: Oh, yeah. And do you know how to enable concurrency? in React. Okay. Noel: Not in React. No. Ivan: ~Uh, so, ~all right, have you heard of the like concurrent mode versus blocking modes, like some of these earlier Noel: [00:21:00] Yeah. I've heard people mention it, but I've,~ I've never, uh,~ I don't do a ton of front end dev work. So ~I've never,~ I've never had to flip any of these flags. Ivan: ~Oh, all right. Um, well, so the, ~~The~~ idea behind concurrency is basically as you described it as, um, no, no, no, no, no. We, we already mentioned several times what concurrency is. Sorry. I'm thinking out of places. Yeah.~ So concurrency is something that takes indeed an expensive render and makes it non expensive. And this is something React originally has wanted to ship, just enable for everyone by default. So ~back when in, back,~ back when React 18 was created, the React team was talking a lot about blocking mode versus concurrent mode. And the idea was that all the apps, That are written nowadays, they are written by default, or that are written with rex 17, 16, 15, below. They are written in the blocking mode, ~or actually there was,~ that was called technically the legacy mode, but ~I'm not, I'm not gonna, like, that's,~ that's just like the ~trenches,~ trenches that do not matter. All the apps are written in the blocking mode, which is any time. You trigger any render, that render blocks the page. If you have a render that has to update 500 components and each of these components takes two milliseconds, then the page will be blocked for a second. And that is always a terrible user experience, right? The [00:22:00] page is frozen. The user cannot do anything. what React concurrency was about and what concurrent modes was supposed to introduce. Was that with React 18, you would update your app to the new APIs, like you would eliminate all the lifecycle methods. You would switch to the new, like you would switch to ReactDOM. createRoot. render instead of ReactDOM. renderRoot. ~Um, ~you would maybe update a few other APIs. And then once you do that, Your whole app will magically switch to the concurrent mode, which is anytime you have to click a button, the button, even if it needs to update 500 components and each of these components takes two milliseconds, the page would not be frozen anymore because instead of running all this work in one chunk, React would split it into chunks of five milliseconds. And after each five milliseconds, React would give the control back to the browser. So the page would never be frozen for more than five milliseconds. Thanks. And so if a user tries to click something during this [00:23:00] rendering, React would be able to handle that render right away because, again, the page would not be frozen. So that was the idea behind the concurrent mode. ~The challenge, ~so that was the original React vision. The challenge with that vision is that it ended up being a bit too hard to turn into reality.~ Um, I do not remember the reasons of the top of, ~I do not remember all the reasons of the top of my head. I think ~it was, a part of it was,~ it was just that ~the migrating to concurrent mode, migrate,~ migrating existing apps to concurrent mode was quite a lot of effort. ~Um, ~maybe there were other reasons, but what React went forward with instead is instead of upgrading the whole app to concurrency, which it ideally wanted to do. It's. Started to enable concurrency gradually when you use this or that specific React API. Noel: ~Hmm.~ Ivan: And so nowadays the React APIs that enable concurrency are two APIs that like explicitly tell it. It's use transition and use deferred value. ~If you've read about these APIs, I know, have you, have you, have you read about this or heard about these APIs or~ Noel: ~Use trend. I've, uh, I've used used transition. What was the second one you mentioned?~ Ivan: ~uh, use deferred value.~ Noel: ~I haven't used that one.~ Ivan: ~Oh yeah. But yeah, like ~if you use this API, if you ~like ~go and written docs of this API, you [00:24:00] would notice. ~You would see that you would see right away ~that these docs are talking about concurrency and ~how, ~how this API is ~make, ~make the render concurrent and unblocking. But one more API that enables concurrency invisibly is the Suspense API. And I think that's also a pretty cool way of react, just like invisibly upgrading everyone ~to, ~to better performance. And I don't know, ~do you, do you personally use, so you, you, ~you mentioned you're not really a frontend developer, I don't know how much frontend you do nowadays, but. Noel: ~I do some. Yeah. Yeah. So ~I guess my understanding of the Suspense API was that it effectively ~like ~allowed batch updates to sets of components in the front end. So like nothing would render until everything within a Suspense boundary was completed. Ivan: ~yeah, I think it's, um, shoot, there's a Suspense list that, well, it's~ ~I'm yeah. ~I think ~that's roughly it's, um, like ~the idea behind Suspense is you can use Suspense for some data fetching or for some lazy loading of some components. ~Right. ~And until whatever's inside the Suspense boundary has loaded, the Suspense would render some fallback. Noel: ~Yeah. ~Yeah. There's like a loading or, ~you know, whatever, whatever you want, like ~an empty state or a loading state or whatever you want to have there. Then once everything inside is complete. Yeah. It's like, okay, here, we're done. I'll give you the full tree. Ivan: ~Yeah, pretty much. And that's like the main goal behind Suspense. It's, it's an architectural pattern. It's not a performance pattern. It's an architectural~ Noel: ~Right. Yeah.~ Ivan: ~But a really cool thing about Suspense the track has done is if you have a server rendered tab, or maybe statically SSG static,~ Noel: ~Like a generated. Yeah.~ Ivan: ~yeah, I'm forgetting what, what SSG means, uh, like how, like what, what, what the letters mean. Um, but if you have a server rendered tab and if any of. If any parts of that app are wrapped with Suspense, then these apps would also hydrate concurrently. And this happens totally invisibly to the user.~ ~These, this is, this becomes possible because, because of the way Suspense is designed, um, like it has a pullback. So if something, if the user does something that's unexpected to react, like for example, it clicks. It clicks as, uh, the user clicks a Suspense boundary that wasn't hydrated yet. And Rag goes like, okay, oh no, I need to practice and Rag goes like, okay, oh no, I need to handle this click some in some way.~ ~But I still don't know how, because this part has not been hydrated. It's like. Greg could always, um, render the, no, sorry, I'm going in the wrong direction. Let me redo the last minute. Sorry for this pauses. I'm like, whenever I'm giving a talk, I'm, I'm like, I basically pre write all the texts to give the best explanation. Now on the~ Noel: ~when you're on the spot at stuff. Yeah, for sure.~ Ivan: ~yeah. Um,~ Noel: ~I can cue you up with a question if that'd be helpful, or I can just give you time, whatever you'd prefer.~ Ivan: ~Yeah, no, give me a question, yeah.~ Noel: ~Yeah. So,~ ~That's,~ that's my understanding ~of,~ [00:25:00] of use Suspense. ~How does, ~how does that ~like ~loop back into this concurrency thing? Like,~ Like, like what, how, how is use, use Suspense or ~how is the Suspense boundary rather like leveraging concurrency to make these renders happen? Ivan: ~So, ~what React has done with React 18 is when you have some parts of your app wrapped with Suspense, React also applies concurrent rendering to these parts, which is, again, by default with React 17, React 16, all the previous versions of React,~ um,~ when you hydrate the app, the page will be frozen for the duration of the hydration, ~and ~What this means is that in React apps, hydration is often the most expensive JavaScript operation that ever happens because when you hydrate a page, React ~has to render every single, ~has to execute every single component that's present on the page. And ~that just takes, ~the bigger the pages, the more time that takes. So what Suspense does is when you have some parts of the page wrapped with Suspense, First react applies concurrency to this part of the page, ~which is like, let's say you have a page with like header. Uh, let's say it's, ~let's say it's ~a page.~ ~That's like a block lists listing page, right? A page,~ a page ~with~ that lists blog posts and you have. several sections. You have a [00:26:00] header, you have a footer, you have,~ uh, like ~a card ~for each, ~for each blog post, and there are like five blog posts. so let's say you wrapped your header with Suspense, your footer with Suspense, and each blog card with Suspense. What would happen then is React would still hydrate the skeleton of the page in the blocking manner. So it would still freeze the page. But then for every part of the page wrapped with Suspense, React would go and hydrate that part of the page in a non blocking way. So if your page had, for example, again, 500 components and each of these components was taking 200 milliseconds to render, and previously you would hydrate all of that without Suspenses, then all this hydration would freeze the page for a second, 500 components, each two milliseconds, a second. But now if you wrap some parts of the page with Suspense, it's going to Only the skeleton would be hydrated blocking lane. That's for example, like a hundred components and that would freeze the page only for 200 milliseconds. And then once [00:27:00] the skeleton is hydrated, React would go over each Suspense boundary and hydrate it independently, but hydrate it in a concurrent manner. And that would not freeze the page anymore because again, React would split this work into chunks of five milliseconds and give the control back to the browser ~after five, ~after each five milliseconds.~ This, ~this happened completely invisibly to the users.~ Um, ~I think most of the developers I talked to about this,~ they, they, they didn't, ~know about this happening. And I ~really, ~really like this because ~you just like, just like with company did mount, ~just like with the move from company did mount to useEffect, you would make some change for the architectural reasons. Not because it makes you up faster, but because it makes your app ~like ~easier to maintain or easier to write. what would actually happen under the hood is that your app would also become faster due to this invisible hand to fret performance. Noel: ~Yeah, it's, it's really, it's,~ it's very nice when that can happen. Like ~the, the, ~the function of the thing cleanly maps to ~like, Oh, ~the utility that the library is able to leverage underneath the hood. It's like, this is the intuitive thing that developers will want to reach for. Like this boundary [00:28:00] here is something that there's a lot of. Utilities like this is just how you would not natively write or intuitively write a loading statement. It's like, Oh, we can leverage the fact that they're doing that to make our rendering as optimal as possible. That's neat. ~Um, let's see, ~we're getting a little low on time. ~Um, ~is there anything ~kind of ~coming~ down, down the pipe that you're excited~ around the pike that you're excited for that you've been looking at,~ uh, ~on the ~performance ~performance front? Ivan: ~Um, ~yeah,~ I'm, ~I'm really looking forward to Sarah components. I think a lot of people are looking for a~ a certain components~ to serve components and may as well.~ I ~I'm really excited about them taking a ton of logic and moving them to the server so that you never need to run that logic on the client that should make hydration much cheaper that should make subsequent renders much cheaper. I'm not looking forward to the design aspect of server components, which is,~ uh, ~request waterfalls,~ uh,~ React says that this is not going to be a major issue because ~like ~request waterfalls and the server are probably still going to be fast, but I have worked with teams that have slow backends and each request takes 300 milliseconds. And when [00:29:00] you do them as a waterfall, instead of being paralleled, then suddenly you've got a 900 millisecond server delay. ~Okay. Um, ~like if it's three requests,~ just, ~just because of waterfall. ~So, I'm, ~I do have a feeling that's going to become a ~major, ~major subject,~ uh, ~later on. But, ~I think, ~honestly, the biggest thing I'm excited about is, server actions. Noel: Oh, okay. Ivan: And the reason for this is,~ you know, the biggest pain, ~the biggest pain of React apps for me and ~also, but ~also a lot for a lot of like accessibility folks,~ um,~ I know browser standard ~folks, ~folks, just generally folks living in countries with slower internet is that React apps, they kind of lie to the user, right? ~You would open. If you open Airbnb, for example, and Airbnb ~if you open Airbnb on a slow connection ~and you try to click, like, so~ Airbnb will load right away. You would see the search field, you would see some listings, etc. But if you try to click that search field on a slow connection, nothing would happen because the page's JavaScript has not loaded yet. You're clicking the search field and~ well, ~there's no JavaScript to handle that. The app looks like it's interactive, but it's actually lying to you. ~It's, ~it's not working [00:30:00] just yet. So what server actions allow to accomplish is they you to do this kind of progressive enhancement, which is when all the JavaScript has loaded, your page would work just as you expect it to work. You like, it would be fully interactive. You'd click the search field and,~ um, ~like some model would open or I don't know, whatever, or you'd click the search button, it would take you to a different page, ~but before JavaScript loads, would a server action, ~but before JavaScript loads, what forms with server actions allow you to do is they allow you to write code in a way that react with, to realize that you To a separate old school call, like ~navigation, ~navigation to the server. maybe that sounds complex, but the idea is that before JavaScript loads, what would happen when you type into something into the search field and click the search button, you would have indirect lens. You would have a form field with a [00:31:00] server action and what react would server render that to, is it would render that into a form that would On submit redirect you to a different URL, but this time is going to render the concrete search result that you're looking for. And this would only happen before JavaScript loads, because after the JavaScript loads, like that would become fully interactive and like everything would happen within the same page, like no redirect, nothing like that. But before JavaScript loads, ~some parts of the page ~with some reactions, some parts of the page would be able to become interactive. Immediately and not because they are interacted by some JavaScript, but because they're interactive, thanks to this old school technology that's been around for 20 years. And that's works really, really well for users with slow connections, which is like half of the year still. Noel: ~Yeah. Nice, nice. Cool. Yeah. It'll be, it'll be nice to see ~hopefully ~like again, ~like we can ~kind of ~continue that trend of making that easy to leverage and intuitive for devs to kind of stumble into the correct thing. And like, I think there's a lot of work being done there, but we're [00:32:00] starting from a pretty tough point. So~ like, ~hopefully we can get to continue that balance. Cool. Well, again, it sounds like there's still a bunch ~we could, ~we could chat about, but we need to wrap here. ~I haven't, uh, ~but thank you for coming on and talking performance with me for a little bit. It's been a pleasure. Ivan: It's been a huge pleasure for me as well. Thank you. ~Thank you for the invite, Tony.~ Noel: Of course. Of course. Take it easy.