React Performance Debugging Masterclass

Rate this content

Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).

Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.

Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.

(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)

170 min
19 Jun, 2023


Sign in or register to post your comment.

AI Generated Video Summary

This Workshop on React Performance covers various techniques and tools to analyze and optimize app performance. It includes using Chrome DevTools' Performance and Profiler panes, analyzing CPU activity and function calls, identifying expensive renders and components, debugging with 'why did you render', and optimizing React code and effects. The workshop also discusses the use of ConsoleTime API for profiling, optimizing the state manager, and analyzing memory performance. Overall, it provides valuable insights into improving the performance of React applications.

1. Introduction to React Performance Workshop

Short description:

Welcome to the React Performance Workshop. Today we'll talk about approaching React performance issues and analyzing a slow app. We'll use Chrome DevTools' Performance pane to record and analyze app performance. Let's start by looking at the CPU row to identify where the app is slow. Then we'll zoom into a recording to analyze the main pane during CPU spikes.

♪♪ Welcome to the React Performance Workshop for React Summit. I'm very glad to meet all of you and to talk to some of you already. So I'm Ivan, I'm a Google DevOps expert. I'm a web performance engineer. I've been working on performance for five years at this point. I've worked with companies like Google, CNBC, Framer, etc. And today we're going to talk about the ways to approach react performance issues when your app is slow and you need to figure out why. But you're not really sure about what the next steps to take. Which was pretty much me at the beginning of my career.

And so to start... Oh yeah. Work stuff. So the workshop lasts for three hours, right? We'll have 10 minute breaks every hour. We have time for Q&A in the end. If you have any questions in the process, feel free to... Actually, sorry, I think this meeting is a bit too big for Ani to ask. So yeah, you did not see that. If you have any questions, feel free to ask in the chats or give them for the Q&A at the end. And also, in case you're the person who likes to keep notes, there's a link, dash Ares for notes, which, if you go to that link, you'll see this document called React Performance Workshop, Collaborative Notes. And here you'll find the link to this doc, to the Google rep or to the Discord chat, where I'll drop the links after this workshop. And also, if you keep any notes or if you get any questions that you want to ask later but don't want to forget, feel free to keep them in the notes here so that everyone can collaborate on the notes and everyone benefits.

And so to start, let's go and look at a slow app right away. So if you open this repository and if you clone this repository, which I already have cloned, and you open the notes directory, and you install the dependencies and then run yarnstart, you'll get this basic note taken up. You, like, if you attended... If you attended the talk that I was giving on rec time, it's about rect concurrency. You might have seen this, which is not a coincidence, but we're not going to be talking about rect concurrency today, we're going to be talking about other issues. And this app has performance challenge. Specifically, if you create a few hundred nodes, like 500, 600, 700, and then open any of these nodes, and then try typing to the editor, the app would feel pretty laggy, pretty slow. So here I'm typing to the editor in the app, and the app feels laggy. Now you can't really see that, right, because it's my laptop, it's me typing, it's me feeling this. You can't really notice it being slow. So one thing I'm gonna do for you to see when the app is slow is I'm going to go to DevTools, More Tools, Rendering, and I'm going to click the Frame Rendering Stats checkbox. So when I click Frame Rendering Stats checkbox, I get in the top left corner this thingy, which basically shows me when the Main Thread is busy or idle. So right now, if I do something quick, like scroll this list, I could see that my frames per second rate stays high, and there are some reds and yellow lines appearing, which means that the Main Thread was busy with some work for a very short period of time. But if I try typing into the editor, you could see how the frames per second rate is falling immediately upon every key press. And you can see how the Main Thread starts to get filled with reds. Which basically means the Main Thread is blocked. So even if I just type a single letter, you could see the Main Thread getting blocked for some prolonged period of time. Or if I just type multiple letters at once, I just typed a bunch of letters and the app was so busy, it didn't even update the frame rates. Chart, is this a chart? Maybe this is a chart. But anyway, so we have a performance issue. I'm typing to the editor and the app is slow. Now, whenever I have a performance issue, the first thing I always do is I go to ChromDevTools, I open the Performance pane, and I try to record whatever's happening in the app during this performance issue using the Performance pane. So what I'm going to do now is I'm going to open the Performance pane, which I already have open, I'm going to click Record, I'm going to type into the editor, but not a bunch of letters at once, like I just did. Because that's going to trigger a lot of JavaScript, supposedly or whatever, whatever it is that's slowing the app. But just one letter and then wait a bit, another letter and then wait a bit again, and another letter, and then stop the recording. And once I do this, I would get this performance recording that will show me everything that's been happening in the app during the recording, while I was typing on these keyboard buttons. So if you see this for the first time, this might be a little overwhelming, because there's like a lot of stuff happening here. But the only actual two areas that we need to focus are the CPU row, which shows when the app, when the main thread was busy, and the main pane, which shows what exactly was happening in the app when the app was busy. So now the first thing I'm gonna look at, so I've just recorded, I've just did performance recording, right? So the first thing I'm gonna look at to try to figure out what's going on in the app is I'm going to look at the CPU row, and I'm going to try to figure out what exactly, or figure out where exactly the app is slow. And I can see, like, this is pretty easy to see, right? When the CPU row is empty, that means the app was idle. When the CPU rows filled with some color, like yellow in this case, for JavaScript, this means the CPU was busy running some code. So in this case, I see three spikes of CPU activity, which, I guess, corresponds to me typing into the editor. So what I'm going to do next is, I'm going to zoom into one of the recordings and switch to the main pane and try to figure out what exactly is happening in the main pane during the spike of CPU activity. And so let's take a look. So we have a spike of CPU activity. The spike of CPU activity took 215 milliseconds. In the spike of CPU activity, we have two rectangles that indicate what's been happening at the very topmost level. So we had a task, a single task, which means a single and interrupted period of time when the browser was processing something. This task took 200, sorry, 215 milliseconds. And during this task, we get two events. One, the key down event, which took just three milliseconds to handle. And another one, the key press event which took 212 milliseconds to handle. So the first one is probably not super important because it's, so first of all, look so far. So far so good, right? This matches what they get in the app. I typed into the editor, we got the key press event, key down event. So the key press event is cheap. I'm going to ignore it for now, but and I'm going to focus on the key press event, which is, sorry, the key down event is cheap.

2. Analyzing Key Press Event and Function Calls

Short description:

The key press event is the expensive one. It triggers the text input event, which then triggers the input event. These events lead to multiple function calls, mostly from Ragdoll development and the React core.

So the key press event is cheap. I'm going to ignore it for now, but and I'm going to focus on the key press event, which is, sorry, the key down event is cheap. I'm going to focus on the key press event, which is the expensive one. So the key press event triggered. The key press event in turn triggered the text input event. The text input event in turn triggered the input event. The input event triggered some function call, which seems to be mostly this textAreaInputPol function, which is something, I'm not going to dig into that now. I'm just taking a high level look. And then the runMicroDosks rectangle, which took 209 milliseconds, so still most of the time. So runMicroTasks triggered another function call, which in this case seems to be coming from Ragdoll development. And that function call triggered another function call, and that function call triggered another function call, and then that function call triggered another function call. And what I see here is a whole lot of text input events function calls coming from Ragdoll development, coming from the React core.

3. Analyzing Functions and Using the Rack Profiler

Short description:

When analyzing the CPU activity and functions, the color of the functions indicates they are coming from the same source. The app is slow when typing into the editor, triggering a JavaScript task and a chain of events that execute a lot of racked code. Seeing a lot of racked code in the recording indicates the need to switch to the Rack Profiler. The Profiler tab provides an overview of what's happening in the app. Recording with the Profiler tab reveals the list of renders and the components rendered during each render. Questions from the chat clarify the interaction and the ability to record multiple iframes with the performance pane.

And so if I look through these spike of CPU activity, if I look through all these functions, and by the way, I actually don't really need to look through all these functions, because if you look at these functions, you'll notice that they have this pink, pinkish, I don't know, light pink color. So this color does not mean anything in particular, it's random. But what I know when I see functions having the same color, like all these functions in this case, what I know when I see functions all colored with the same color, I know that these functions are all coming from the same source.

So what Chrome DevTools do is, whenever we have functions coming from different files or from different sources, they try to color these functions with the same color if they're coming from the different file. So in this case, I don't even need to go through all of these functions to confirm that they're coming from Rackdown development. I can just click on this one, click on this one, click on this one, confirm that they're all coming from Rackdown development, and then assume that probably the rest of them are also coming from the same place. All right.

So here's what we've learned so far. We have a slow app. The app is lagging when I'm typing to the editor. When I'm typing to the editor, when I type a single letter into the editor, I get a single JavaScript task that takes around 200 milliseconds. That JavaScript task triggers a key press event and the key press event in turn, through a bunch of other events, triggers a whole lot of racked code. Now, at this point, when I see a lot of racked code, I don't need to know what this code does. I don't need to be familiar with it. I don't need to know racked internals. What I know when I see a lot of racked code in the recording, when I see the racked code taken the most of the recording, is I know that I can switch to a different tool that will give me a better overview of what's happening in the app, and this tool is the Rack Profiler.

So, Rack Profiler is a tool coming from the Rack DevTools extension. So, if you just Google for Rack developer tools and install this and then reload the DevTools and reload the page, you'll get this work, I'm pretty sure most of you have this installed already. But, what I'm going to do is, given that most of the code here, most of the code here is coming from Rack, I'm going to switch to the Profiler tab, and I'm going to re-record the same interaction I've just done using the Profiler tab, the Rack Profiler tab. So, here's what I'm going to do. I'm going to click Record, which is not going to work for some reason. Now it works. I'm going to click Record, I'm going to redo the interaction again. One, two, three, and I'm going to stop the recording. And then, once the browser stops processing the recording, I would get a bunch of new additional data about what's been happening in the app while React was running. And so let's see what we have here. We have, first of all, we have the list of all renders that happened in the app. So I click the keyboard button three times and that apparently caused three renders. I can see three renders in this row one, two, three. All these renders seem to have taken roughly the same amount of time. So two, three, three, one, nine, nine, two, one. Okay, they're probably pretty similar. And then in every render, if I click any of the renders, I would see all the components that got rendered in the app during that exact render.

And I see some questions in the chat. Let's type it here. Tittered says you did not type in the search box, or the profiler did not record the same thing. So I was actually like, for this workshop, I'm not actually typing into the search box. So when I was doing the talk, I was typing into the search box. Here I'm typing into the editor. So it might be easy to confuse it because like the same app, right? But yeah, that was the same interaction. I was typing to the editor. It's the same thing. Sorry. Sorry, I hope I pronounced that right. Sarayo asks, is there any way to do that when the application isn't one iframe? So when you have multiple iframes, I think you have a way to expose the React dev tools. So, oh yeah, sorry. You could answer this in regards to the performance pane or in regards to the performer pane. So with the performance pane, the performance pane records all iframes at the same time. So if you go to any website that has an iframe, like maybe I can go to and like, let's see whatever this was. I don't remember what this was. Like this thing is an iframe, right? No, I don't want to to the Pro. And like, if I record what's happening with the performance pane, like I probably need some JavaScript right? Let's do, hold on. No. Okay. I'm going to write that manually. Let's do this setTimeout. That's runs every second and then, blocks the main threads for a hundred milliseconds. It counts now, performance now. While now, plus hundreds is less. Hold on. Now plus 100 is more than performance. Now. Okay. They should supposedly look at the main thread for a hundred milliseconds. Now if I setInterval. So now if I record what's happening, so now if I record what's happening with the performance pane, I would get, I would actually get an additional pane in addition to the main pane. So I would get the main pane, which shows everything that happens in the main iframe. And I would get a bunch of frames that show what's been happening in the iframes. And they show you pretty much the same thing. You don't need to do anything specific to enable that.

4. Analyzing App Performance and React Code

Short description:

To debug the recording and make it more legible, zooming out and examining the components revealed that the bottleneck was the NotButton components. Deleting 700 NotButtons and creating 100 improved app performance and made the recording more legible. However, recording with the profiler did not work properly due to potential browser or DevTools bugs. Profiling performance with the profiler or performance pane can be challenging due to these bugs and design decisions. Despite these challenges, we learned valuable insights about app performance and the role of React code.

It just works out of the box. So with the performance pane, it's pretty easy. With the profiler, it's harder. I think you need to do some stuff to expose React's iframe, or React tools. Yeah, you need to, oh yeah. You basically need to do this. I hope this code is not outdated. Oh no, it's 2022. It's probably not. But yeah, like if you add this into your iframe, your dev tools should also work well. And they would even like, let you pick which iframe to choose to profile. So, yep. Now let's go back to the profiler.

So if I record everything that's happening, if I record the interaction that I'm having, I don't know why it's not working. If I record what's happening in the app with the profiler, I would see every component that get rendered during my interaction. I would see all the renders that happened during my interaction. And I would see every rendered, every component that's re-rendered during every render. So in this case, I'm typing into the editor. One, two, three, I get three renders. And during each of the renders, it seems to be like more or less the same components rendering every time, right? I have the app component rendering. I have the darkmat provider component rendering. I have the context provider component rendering. Then I have the nodes-list components for rendering. And then inside the nodes-list, I get some filtering boots and then some node buttons rendering. So at this point, I might get a little confused because what I see here is I see the nodes-list components taking one, so, hold on, actually, am I getting a bit ahead of myself? Maybe, not, no, I don't know, but at this point, I might get a little confused because what I see below nodes-list, I see a few components that get rendered, like a bunch of node buttons, but I also see a bunch of white space. And so if I worked with the components pane before, I know that white space generally means the browser was not doing anything in this period of time. Like, here's the white space, that means the browser is idle. But in the profiler pane, the white space does not have the same meaning. Rack profiler is behaving differently. What happens here is if you have a lot of components, if you render a really, like, a huge amount of components, and all the number of components cannot fit into the whatever UI we gave to it, Rack will just keep rendering these components. So, what happens here is not that the browser was idle, and the browser was doing nothing. What happens here is we have so many components that Rack profiler just cannot feed them into this space that it has. Like, all these components end up being narrower than one pixel. So, what Rack does is Rack just does not render them at all. So, this is a bit confusing. This might be a bit confusing, but keep this in mind. This could happen if you have huge lists. And so, what I do when I see this huge, like a bunch of empty space, what I typically do to confirm whether that's the case, I just zoom out until Rack renders more components, and I could actually confirm that, okay, is this, is this just like, is this some weird bug about the white space? Or are there actual like a bunch of components that cannot be rendered? So, cannot be fit into place. So, here you see, I zoomed out, and the Rack rendered more components. So, yeah, this is confirmation. This was my quick way to verify that, okay, this white space is not the browser being idle, this white space is just Rack not being able to fit the components into the recording. So, but this is really annoying to debug, right? Going I can't see the components. So, what I'm going to do then, what I'm going to do now to make it easier to debug, is I'm going to zoom out, and I'm going to try to look through, oops, different app. I'm going to try to look through the concrete components that I have here. And, okay, all of these components seem to be node buttons, node button components, right? So, basically, I'm going to zoom out and I'm going to look through everything that's happening here. And I'm going to try to figure out what exactly is happening in the app and if I can make this recording a little more easier to understand. So what we have here is we have a re-render. During this re-render, we got the app component rendered, the app component rendered and caused the DarkModeProvider to render, the DarkModeProvider caused the ContextProvider to render and the ContextProvider caused the NotList to render and then the NotList rendered and it also rendered the filter inputs and it rendered a whole lot of NotButton components, which if I hover them, I could just see which components that correspond in the app. So like I can hover this and I see that these components are getting highlighted. So this is pretty nice. And so if I move my mouse through this zoomed out recording, which is very annoying, yeah, you have to do that sometimes. I would see that all these components, most of these components seem to be just NotButtons, like this is like 90% of the recording at this point, right? So maybe what I can do to make the recording more legible is why don't I like delete all notes and then create just 100 notes instead of 700 and then try to record what's happening in the app with just 100 notes instead of 700. So the app would probably get faster if the main bottleneck here is the NotButton component, which it seems it is because look like there are so many of these NotButton components just rendering and taking like 90% of the whole recording space. It's probably the NotButton components at the bottleneck. So if I delete all my 700 NotButtons and create just 100, the app is going to get faster, but that might also make the recording more legible. So let's try it. So I deleted all notes. I created 100 notes. Now if I type into the editor and enable frame rendering stats. If I type into the editor, I could still see my main threads getting busy, although for the smaller period of time. And if I go to the performance pane and click record, I could still see the tasks of JavaScript through the keeper as it wins and then a bunch of React code running underneath. But now if I go to the profiler and I record the same interaction with the profiler, which again does not work. I'm not sure if this is DevTools issue, is this my browser issue? If I record the same interaction this time I would actually get no white space because we've got way fewer components and so all of them fit. So this was maybe a little longer digression on solving this, but the reason I'm showing this is very intentional, when you're profiling performance with a profiler or profiling performance with a performance pane or when I'm profiling performance with a performance pane, when I'm profiling performance with a profiler, I'm often getting stuck over browser bugs or dev tools bugs and they're very annoying to work around and they're also very confusing if I don't know what exactly is happening. So this thing that I just showed, it's one of the profiler, maybe bugs maybe design decisions that's to me, it's very confusing. So the reason I'm showing it here is because you might stumble upon it and if that would be me doing this for the first time, it would definitely trick me over. All right, so here's what we learned so far. We made a performance recording of what's happening in the app, when we have a slow interaction. We saw that most of the code, most of the stuff that's happening in the app during the slow interaction is the React code. We went through the profiler pane.


Analyzing App Performance and Component Rendering

Short description:

We recorded the slow interaction with the Chrome DevTools and learned that most of the activity was due to direct code. We used the React Profiler to analyze the components that were re-rendering during the interaction. The nodes list component with its children took the longest to render. To further debug the performance issue, we enabled the 'record why each component rendered while profiling' option in the Profiler settings.

We recorded what's happening in the app with the profiler pane and we learned what exactly is happening when the React code is running. We learned all the components that are rendering when the React code is running. So my next question now is, okay, but why exactly is this all rendering? What can I do to actually fix this? But, before we get that, do you folks have any questions about what we went through so far? This is the good time to unmute and ask if you want to unmute and ask. Do you have any questions about the performance pane or the profiler pane?

A question? Yeah, go ahead. My name is Gabriel, I got in a little bit late and I've seen all your things, so I just wanted to ask if you could if you would get to later on do a review on what you have just done so far, you need to touch it in an hour. If you're going to touch, sorry, what? I missed it. I was having issues connecting, so just what you have done so far, just a little review or something like that, either now or later. Oh yeah. I don't think we're going to go through that from the very beginning again, but we are going to have more examples in the workshop. So more slow apps. And by the way, this is a very... Is there anyone who actually curious? Could you write into the chat, could you send a plus into the chat if you knew like 95% of what we went through so far and minus if you knew less, so like plus if you already know 95% plus what we... Let's see a bunch of pluses, a bunch of minuses. Right, thank you. Yeah, so like what's happening now is like, I have this like very, very isolated example, right? It's not with a single trauma issue. So this is not the only example we'll be looking at today. After this, once we're done with this, we'll take a look at a real world app, a real world open source, very complex React app, and we'll see how performance debugging might look. We'll see how I approach performance debugging of these apps. And so, well, hopefully, if you know everything that we went through so far, hopefully that might be, you'll learn something new from that part. So I'm- I have one more question. Sorry. So in case I see white spaces, and it's not because of a displaying issue, my interpretation would be that it's because the parent is busy. So, in your example it would be the node list is just doing too much work. If I see nothing below it, but it appears to you in the performance profiler. Is that correct? Oh yeah, I think so, yeah. So I think the way React DevTools work, and by the way you see, in DevTools you see two numbers, right? Like nuts list 0.7ms out of 45ms. So the first number means, like, how much time it took to render these components specifically. The second number means how much time it took to render these components with all its children. And rendering here means literally calling this components. Like if this is functional component, React literally calls this component to render it. So if you have a functional component, that's if you have a functional component that's doing it'd be too much stuff. Like the nuts list for example, has like codes that takes 10ms. What React DevTools would do is like in the beginning, I think they would remove space that's like proportional to this 10ms. So you get a bunch of free space in the beginning and then you would get like all the components that are actually rendered. But positive free space in the beginning there should not be any more free space from experience. All right, thank you. Ramiro asks, are there any other options for profiler tools? Yes and no. So I actually have this slide in my slides with like all the debugging tools that I use while profiling. So my two primary tools are the performance performance pane, direct profiler and direct profilers, unfortunately buggy at times and I just have to deal with that, but we'll also look through other performance through other debugging tools like these two and one of those through the rest of the workshop. They do not replace the right profiler, but they do compliment it and they help to make profiling easier. Jory asks, I tried the performance pane in one of my company's apps, but I see a lot of this weird two letter abbreviations like Z I, X D, T J, C S, et cetera. What do they mean? I think what's happening in that case is just because its minified. Like if you go to like for example and try to, Oh yeah right. I have a... Ooh, hold on Airbnb. Let's try Airbnb, it's the also ranked site. If you just try to record what's happening here, you'll also see all these things. R S, F S, whatever. It's just minified names of functions. My asks, will the workshop be recorded? Yes. The workshop will be available in your rectsummit cabinets somewhere there. I'm not fully sure. You should ask in discord if you don't find it afterwards. All right, great. So here's what we've done so far. We have a slow app. And this slow app, when I'm typing into the editor, so we have a slow interaction. When I'm typing into the editor the app feels slow. And so here's what we did so far. We recorded the slow interaction with the Chrome DevTools, with the performance pane. We learned that most of the stuff that's happening here is direct code. We went to the Recto filer and we learned that, and we recorded what's happening during that interaction with the Recto filer. And we saw all the components that are re-rendering during this interaction. And so at this point, I could already see, I could already start seeing some bottlenecks. So I would look at this recording and I would go through this recording and I would see that the nodes list component with all its children takes 45 milliseconds to render and it's the most expensive part of the recording. And then the component is not expensive on its own because on its own it takes only 0.7 milliseconds, but together with all its children, especially with all the node buttons, it takes the whole hopping 45 milliseconds, which is, it might not seem a lot, but I have an M2 Pro laptop on an average user laptop that's going to be four or six times slower. And so my next question at this point is, okay, but why exactly are these components rendering and what we can do to prevent them from rendering? And so the next step that I do to debug this performance issue is I go to the profiler settings, I go to the Profiler tab, and I click the checkbox that's called record why each component rendered while profiling. And so if I enabled the checkbox, and if I try record the performance interaction, I need to reopen DevTools again. If I try to record the performance interaction that I have, now one, two, three, this time I would see the same tree of rec components. But if I hover any of these components, I would see why exactly each of these components were rendered.

Analyzing Component Renders and Optimization

Short description:

Let's figure out why this component's rendered and what we can do to solve it. The app component renders due to changes, as well as its child components. The notesListComponent renders due to specific props changing. We can optimize by preventing unnecessary renders. The node buttons components also have props that change unnecessarily. We solve this by using callbacks and memoizing the components. By doing this, we debugged and solved a performance issue. We recorded and analyzed the app's performance using the Performance and Profiler panes. Next, we would continue optimizing components and finding unnecessary renders.

And so this is my next step. Let's try to figure out why this component's rendered and what we can do to solve this, right? Okay, so what exactly is happening here? I'm typing to the editor. That causes the app component to render. The app component renders because it's what changed. The app component renders and then its Dark Mode provider child renders and that component renders because its children changed. Then the context provider renders. RegDevTools never show the reason for the context provider, but it's, well, like it's never bottleneck. I typically just ignore it. Then the notesListComponent renders and the notesListComponent rendered because it's notesProp onYouNotesRequestedProp and onDeleteAllRequestedProps re-rendered. Then the filtering was rendered just because its parent component rendered. Then a bunch of Node buttons, all these Node buttons, they have re-rendered because the onNotActivated prop has changed. And so what I'm going to do at this point is I'm going to try to approach to solve the issue that I'm observing. Solving issues is generally out of the context of this workshop. But just as a demo, let's try doing this with this concrete app. So what I'm going to try to do now is I'm going to try... So I see a bunch of components rendering. And my biggest question at this point are there any components that are rendering unnecessarily that it could prevent from rendering and optimize the performance interaction this way. So if I look at all these props that are changing, let's see if any of these props are changing unnecessarily. So first let's see what's the hook one in the app component. Maybe if we can prevent the app component from a rendering, like this whole tree would not render. So if I go to the app components and I look for hoops and I find the hook one, which is the use state hook, I would see that the use state hook is the notes object and the notes subject judging by the name it stores all the notes, right? So when I'm typing hook number one changes, but that probably changes for good because I guess it's the notes, like I don't know for sure, it's okay that I don't know for sure, I'm just taking a high level view or all the components. So app mode, we probably can't optimize it yet. Darkmap provider, this renders because it's children changed, that's never possible to optimize because children are always going to change between the renders unless you do some specific rare like weird optimization tricks. NotesList. Okay, so this one seems to be more interesting. In the NotesList component, I have three probes that get changed. Notes, only notes requested, only little requests. The Notes probe is probably the same, like probably the same object with nodes that have here. I can confirm that later, but the two other probes that they see in the list, the only units requested and only little requested, they look like some event handlers. Right? And they seem to be changing on every render. So when I see this, this is already one of the common performance patterns that are anti-partnerings that I'm often not seeing in the apps. Could you already by looking at this, guess what exactly is happening in the app with these props? Could you tell me what's happening with this props just by looking at this performance recording, with these events handlers changing between every render? Yes, Bruno says they should be memoized. Yes. So just by looking at this, and again, I'm not sure yet. I haven't even looked at the codes, but I'm already like taking some notes for myself that, okay, the app components can probably, probably can't be optimized. The nodes list components, it looks like the one prop here changes for good, but the two props that can be optimized probably can be prevented from being rendered. I could go and I could find where that component is being rendered, and now I could confirm the hypothesis. So I have the all node activated, and on, oh no, sorry, on new nodes requested and on delete all requesteds, and we have these two functions, and yeah, they look like functions that are not memorized, that are changing between every render. So that's the nodes list, but the first prop in the nodes list, nodes, it's the same prop, it's the use hook. That's the use state hook from the app component, and it does seem, it's probably changes, changes for good between renders. Like, I suppose it starts the state, so we can't like, we probably can't optimize this component, really. So I'm going to leave this component for now, and I'm going to take a look at its children. And so what we have here is, we have the filter input components, and we have the a bunch of node buttons component. And all the node buttons component, they all, except the first one, they all have a single prop changing between all re-renders, which is the onNodeActivated prop. And this looks, this looks similar to what we just saw with the nodes list, right? Here we have two props that look like event handlers, that were changing every time, and here we have one prop that looks like an event handler that's changing every time. So maybe there's some function that we failed to minimize or something. Let's go take a look. So I'm going to find a place where the node button component is rendered. I'm going to do that with search, or another way I could do that is by clicking at this component and switching to the components pane, and just copying this source link and going to vscode and going to directly this line, which is here. And if I look at the node button component, if I look at how it's rendered, I would indeed see the OnNodeActivated prop being a function that's created on every render. So how do we typically solve this? Tell me, tell me, tell me, tell me? Yes, we use callback. So in this case, in this case, the function is inside a map, so we can't use callback directly here because you can't use callbacks conditionally, can't use, sorry, can't use hooks conditionally, you can't use hooks inside the map, but if I do, so I can't wrap this function with use callback, but if I do a different thing, if instead of passing the onNotActivated, instead of passing an anonymous function that calls onNotActivatedId, if I just pass this function directly and I pass the id directly and I call onNotActivated with the id from inside the component, then the function reference would probably stay the same. Let's see, the onNotActivated prop comes from the NodesList. The NodesList gets the onNotActivated prop from setActiveNodeId. And setActiveNodeId comes from UseState and the UseState callback is always stable. So this function should always be stable. Alright, so if I just pass this function directly, instead of creating and passing a new anonymous function every time, and if I wrap this component with, or... And if I wrap this component with Memo to tell React that this component should not re-render when its props change, then what would happen then is we would go to the Performance Pane, oh, sorry, we would go to the Profiler and we would try to record what's happening in the app. There we go. And then we would look at the trace and here we would see that all the Node buttons that we were rendering before, they are now gray. Instead of green or instead of yellowish, they are now gray. And if we hard-ended, we would see that they did not render. So, this is one performance issue that we just debugged and we just solved. What we went through so far was we took a performance issue, we recorded it with the Performance Pane, we learned that most of the stuff is happening in the Performance Pane, is the RectCode. Because we learned that we switched to the Profiler Pane and we recorded everything that's happening in the app with the Profiler Pane, and we learned all the components that are rendering. And then we went to Settings and we Enabled Record Which Component Rendered While Profiling and we also learned why each component is Rendering. And then, at this point, my next steps would be to go through all the components and try to find some components that are rendering unnecessarily and try to optimize them in some way, and generally the solutions are not a subject of this workshop. But here we just went and we just solved. Sorry. Generally, the solutions are not a subject of this workshop, so we just went over that briefly.

Debugging Performance and Analyzing CPU Activity

Short description:

We found one of the components that renders unnecessarily and solved it. The React rendering was the main cause of the slow interaction. We recorded the interaction using DevTools and analyzed it using the React profiler. We identified the most expensive components and optimized their rendering. Now, let's move on to a larger and more complex example.

We did not go into that period in a lot of details, right? We found one of the components that renders unnecessarily, and we figured out why by looking at its props, and we went and we solved it. And so now, every key press. Instead of taking 50 milliseconds with 100 nodes, it's just taking only 10 milliseconds. And the app should feel much faster even with 700 nodes. Look, I'm typing and the frame rate is great and the red bars are very very short.

All right, so. Now, all these steps I went through so far, they're coming from my memory, right? Like, this is what we went through so far, is like what I showed so far is how I approached debugging apps, but all the steps I take, they're generally coming from my memory, like I know this stuff and I use this stuff, but these things do not have to come from your memory for you. So for this workshop specifically, I prepared this flowchart which shows my process of debugging performance with the, of debugging performance with all the tools I use and if we looked through this flowchart, we would see pretty much the same thing we went through so far. We had a slow interaction and so what we did was, we recorded the interaction using DevTools without for XCPO Throttling but we are going to enable XCPO Throttling for the next steps. We looked at what's happening during the interaction and we saw that most of the stuff that's happening in the interaction during the interaction is the React code.

Now there could be two kinds of the React codes and I skip this but later during the workshop we'll look at the difference, we'll look at some stuff that the React profiler can capture and some stuff that the React profiler can't capture but in this case, most of the stuff that we had in the recording was React rendering. So we went to the React profiler and we recorded what's happening in the app using the React profiler. We're gonna add this to make more explicit. Then we went to all the components using the React profiler and we tried to find the most expensive components in our case, for the notes list with all its children and specifically the note button components that we had a hundred or 700 components that were cheap on their own, but we had just a lot of them. Then we tried to figure out why these components were rendered and in this case for this I used the Rect tool settings record when component rendered checkbox, which is good in most cases. It does not show how exactly the props change but it shows which props changed often that's enough. And then I tried to answer, how can I render these components less open or render fewer of them, or to want to render them at all. And one of the solution the tablet is to untransfer the metal, which in this case involved wrapping the props with us callback and wrapping the component itself with mimo. So this is just one branch of the debugging flowchart.

Now I'm going to send the link this to the chats. What we went through so far during the past hour was this simple app with a single isolated slow interaction. We profile this interaction, and we learned that during this interaction, most of the stuff that's happening is a reactor render. And the reactor render mostly consisted of a few hundreds or hundreds, depending on how many we had of them, not button components. And so what we did was we figured out why the node buttons component rendered, and we solved that. We prevented them from rendering, which made the interaction much faster. So at this point, I'm wondering, do you have any questions about what we went through so far about this or about the debugging flowcharts, which we'll use later for all the next examples? This is a good time to unmute and ask a question out loud, if you want. No questions. No questions three. No questions two. No questions one. All right. Oh, wait. Oh, some chat. No. Yeah. Okay. No questions. All right. In this case, let's take a look at another example, which in this case is a larger and way more complex rail loadup. So, to use this example, I'm going to go to the widget editor app. I'm going to install dependencies in that app, and I'm going to run yarn start. And there, once the app starts, which is going to take a few seconds, I would get this widget dashboard with a bunch of widgets that I could interact with. So this app is a real open-source app of a client of mine that we worked with in the past. The client kindly landed that app for me to use in the workshops. This app was artificially slowed down, and this app has the following performance issue. If you open any settings of any text field and you open the data type dropdown, which specifies the type of the field, and you change it from the text where you to the same text where you, basically not change it at all, you would get a single relatively slow spike of spew activity, a relatively slow lag. You could see it in the frame rates pane. Now, on this laptop, on my laptop, this actually feels pretty fast, but that's because I'm using Apple Silicon with an M2 Pro processor, so like, of course it's going to be fast. For real world users, this is much slower. So to reproduce that, to reproduce what's the actual real world users are feeling, I'm going to switch to the Performance pane and I'm going to turn on 4x CPU slowdown. And so now if I open the Text dropdown and I select the Text dropdown again, I would see this spike of CPU, CPU activity, I would see this main thread being blocked in the Frame rate pane. You see that I can't move my mouse because it's going to disappear, but you see the red bar and the yellow bar and all the time it's the main thread being blocked. And also, if I try to just interact with the app really quickly after this text input it would just be a leggy and slow and like just very, very, very annoying. So let's try to debug face, performance issue. Now, how do I do this? Let's check the flowchart. So the first thing that I do whenever I have a performance issue is I record the performance issue using DevTools, ideally enabling for CPU throttling to mimic real-user's performance if you use Apple Silicon or powerful modern Intercore processor. So I have the Forex slowdown enabled and what I'm going to do is I'm going to click record and I'm going to reproduce the performance issue. And then I'm going to stop recording and look into what's happening in the app during the slow interaction. So let's see what happens here. In the recording, first of all, the first thing I look at is I look at the CPU row and CPU row shows me when the app was busy. And in this case, I see multiple different spikes of CPU activity, which might correspond to different things. So to figure out which of these I actually care, which of this actually needs, I'm also going to look at the frames, which you see right now. Whenever I'm hovering the CPU row, Chrome is also showing me how exactly the app looked at that concrete moment of rendering. So what I need to find among this few spikes is the moments when I click the text value in the dropdown and therefore, the main thread was busy for a significant period of time. And this, okay, so here I move my mouse and the Chrome shows me that because the hover changed, right? Here I open the dropdown and that causes some CPU activity. You could see that the bars not filled to the top, which means the CPU was not 100% busy. And then here in this spike, in this spike the spike starts and then somewhere through this spike, the dropdown disappears and then the spike cancel. So this seems like the moment when I clicked the dropdown. Let's zoom into it. So this is the spike of CPU activity.

Analyzing Spike of CPU Activity

Short description:

The spike of CPU activity reveals multiple independent tasks that block the main thread and make the app feel busy. Analyzing three large tasks with red highlights and triangles will help us understand what's happening in each task.

The spike of CPU activity took around 6 or 700 milliseconds, looking at this, I could also measure it right in the main pane if I hold the shift button. Hold the shift button and drug lick this. I'd be able to measure the duration which yeah, 786 milliseconds, pretty close. And so let's see what happens during this spike of CPU activity. So what I have here happening is I have multiple independent tasks. Each task is a single uninterrupted period when the main thread was busy doing something. If I get multiple tasks, that means the browser maybe had a chance to render something in between the tasks, or maybe just was idle in between the tasks, but the task still blocks the main threads. And if I get multiple big tasks in a row, that still means the app feels like the app feels busy. So in this case, I have three large tasks. They also could get this. Red highlights and the red triangles, and it's long task. Yeah, well. I don't know. Just in case it's useful, I never actually look at it. But we have three long tasks, and let's try to figure out what's happening in each of those tasks.

Analyzing Click Events and Microtasks

Short description:

When analyzing the recording, I take screenshots to annotate the concrete parts. The first task is the Click Events, which triggers React code execution. The onClick event handler dispatches JavaScript code. The event handler itself is short, but there is more JavaScript code running afterwards. The second part is the run microtasks function, indicating a resolved promise. The callback running is from React DOM development. The next function is the render root sync function, indicating a React render. Following the flame chart's first branch reveals the React render. The commit root function includes commit mutation effects, commit layout effects, and flash passive effects. Depending on the time each function takes, different debugging approaches are used.

And one thing I like to do here, when I have a lot of stuff at the same page, is I like to just take the screenshot of whatever's happening in the app. And I like to save that screenshot into a, why is it transparent? Wow, that's some... Oh, right, it's taking a screenshot of... Ooh, it's taking a screenshot of me screen sharing. Wow. Right, now I have the screenshot of the window. So what I like doing is I like just taking the screenshot of the recording that I'm working with and then saving it somewhere and just annotating it straight on the screenshot to remember the concrete parts that's... Like the concrete stuff that's happening during the recording.

So let's do that. I'm going to switch to the debugging flowchart and I'm going to throw a rectangle. Okay, like this is going to be my playground area with the... where I'm going to annotate my screenshots and I'm going to just drop my screenshot right here and try to annotate it right here. And so let's look what's happening here. So the first task that we have here is the Click Events. I click the drop-down and the browser starts handling that click. During that click, I get two smaller parts, two smaller rectangles, the Function Call and the Run Micro Tasks. So the Function Call seems to be mostly some React codes. React code. React, React, React. Event React, click. Compile code. Call callback. So most of this is some React code triggering the synthetic React click events, then compiling some code for 20 milliseconds as well. And then calling some callback and then actually getting to R code, which I could notice that it's R code because it's a different color, but then R code only takes like 12 milliseconds out of this time. And so, okay, what I have here is I have the onClick event handler that apparently dispatches, like handles me clicking in the dropdown and then dispatches all this JavaScript that runs afterwards. And I might just look at it just to know what what there is. So this is an onClick in the dropdown. Okay, just handles some option click handler. This seems to be some generic dropdown. Then, okay, some anonymous in the dropdown. Oh yeah, this is option click handler, useCallback, set is selected, set is open, onSelect, onSelect calls for functions, and then dispatch setState is probably the setState callback. And then the onItemSelect is my actual application logic, probably, updateProperty. Oh yeah, and here we get to the application logic, which is onPropertyChanged something something, or maybe this is it, onPropertyChanged. Okay, so there's just some onClick event handler logic happening. And the event handler on its own is really short. It's just 12 milliseconds. But what's happening afterwards is way more Javascript. And I suppose at this point that all this Javascript is happening because we, the onClick event handler did stop it. But I'm not going to dig into that yet. I'm just taking a high-level overview of everything that's happening, but I am going to annotate it in the trace, in the screenshoot that I have. So let's make the annotation. Yellow, no field. The first part is just the event handler. So that's all. All right. What is the second part? The second part is the run microtasks function. Now, like whenever I see the run microtasks function, what's the typically means is that there was some promise that got resolved. And what's running under run microtasks is some promise callback that's got scheduled but some other codes and like now the premise resolved and so that code is running. In this code, the callback that seems to be running is coming from React DOM development. So I could click it to see what exactly gets scheduled. And I would see function called scheduled microtasks coming from React DOM development that schedule some function. Um, so I don't really need to know this. Okay, that's just some rect internals. I'm going to keep going through this, but then at some point, I would start, like I'll keep going through the rect internals and notice one of the three function names that I remember really well because it's really helpful during my performance profiling. And the first of these functions is the render root sync function. So whenever I see the render roots in function in the trace, what I know is that everything under this function is a render of rect components. You could find this in the rect internals reference. For all relevant versions for rect, like rect 16, rect 17, rect 18, whenever you see the render root sync function in rect internals, that means that a react render is happening. That react is rendering all the components. And that in turn means that whenever you see these, to figure out what's happening here in this part of the recording, you should follow the first part, the first branch of the flame shirt because what's happening here is the rect render. Now, the next function that they have here is commit root, and under commit root, I have commit mutation effects, which I actually don't know what this does. I think it just modifies the DOM, based on the name. Then commit layout effects, which is the second function I know really well, which is the function, that triggers or views layout effect company that mount and company data update callbacks. And then I have the third function, that which I remember really well, which is called flash passive effects, which calls all the use effect functions. And I know these functions really well, because depending on how much time each of these takes, I would debug what's happening here differently. If most of the time is taken by the render root sync function, like it, like it was in our previous, not taking app and like it's still looks, looks to be the case here. If most of the time is taken by the render root sync function, that I'm going to follow the first branch of the flowchart. And I'm going to use the React profiler. But if most of the time is taken by commit layout effects or flash basic effects, I would follow the second branch of the flame charts, and I would try to figure out what effort this are and how to prevent them from running. But we'll get to that later.

Analyzing Render Time and Task Execution

Short description:

Most of the time is spent rendering React components. The process used here helps understand what takes the most time before deciding what to focus on. The first task is annotated with colors for React render and effects. The second task consists of function calls, Redux SEGA calls, and some low-level code. The third task runs because of a fired timer, with a convenient arrow feature to locate the timer installation.

At this point, most of the time seems to be spend in the render root sync function. And that means most of the time is spent rendering React components. And I'm just going to save that's in the annotation as well. So again, like not digging deeply, just collecting the oral picture to figure out what takes most of the time and where to start to start from first. Because actually like one thing I used to do when I was just starting to do performance is I would find the first thing that I seem to understand. I'll like, Ooh, this seems to be like the component to recognize let's dig into it. And then we'll just spend a bunch of time trying to optimize it without actually understanding how much time it takes. And so this process, which I, which we do here, which we do now is the process that I use to understand what actually takes the most time before picking the play before picking what to focus on. Well, this is really inconvenient to use. Actually use a different app for this typically, which is just an iPad drawing app. But all right, this is like let's make it, let's make it red for the React render. So we have red equals React render. And then this parts are just some effects right? Commit layout effects one, commit layout effects two, FlushBase effects three, yep. All these things are just effects. Let's make them green. Do we have to do this? People on this call, sorry. This is, this might be confusing. Depending upon the kind of by the stores. Green is going to be the React's effects. All right, so this, this was the first task. I just annotated the first task. Awesome. Let's continue with the second one. So the second one, second big task, it consists of two things. First, there's some function call, which takes 16 milliseconds and some broker from worker to whatever that is. Broker, I don't know, seems like some low level code. Then, ooh, a bunch of Redux SEGA calls. Something differently colored. Well, there's some, that's just, okay, something also, some low level codes, single observable. Yeah, I don't know what that is. Some library, central reducer, reducer reducer. Alright. This seems to be the app code because it has a slightly different color and because the name finally doesn't look like function code. Oh, this is just a proper ones for reducer. Right, that's still, what about this? No, that's emir. Emir produce still function code. Okay, function code, emir, emir, emir, emir. Anything new here? No, no react redux, react redux, react redux. Okay, so what do we have here? These 13th milliseconds seems to be just some mostly redux related code, right? So again, I'm not going to dig deeply into that. I could figure out what action that is. Like what's actually happening inside redux. Why is emir taking so long? It's probably taking so long because there's some big objects it's duplicating or whatever. But at this point, I'm just going to put a note. This thing is just some redux code. All right, let's see next. Next we have, ooh, we have a pretty similar picture by the way, similar to what we just saw. What we have here is we have more regedom development code and we have, again, renderRootSync, commitRoot, renderRootSync, commitRoot. renderRootSync, commitRoot, renderRootSync, commitRoot. The same thing that we saw in the previous task. So whenever I see this, I actually get really happy because this means we're probably running the same code twice. Which means that if we could just figure out why it's running twice, we could prevent it from running twice and make it save 250 milliseconds just by doing that. So, in this case this does not guarantee that there are the same components rendering, right? Because these functions are just some regedom development functions, but just the shape of these things being very similar, like look at this, this thing has a long leg. This thing has a long leg. This thing has a thick leg. This thing has a thick leg. Like all these things looking similar that this already tells me that, ooh, okay this looks like probably the same code running twice. Which is, I'm happy for this. So, let's just look into confirm this. Render root sync, that's a React render. Commit layout effects, that's use layout effect company did not company to the data base. Flash passive effects, that's use effect. Again, render root sync, commit layout effects, yeah, seems to be mostly the same stuff that we had there. So I'm just gonna select all these four and move them here and adjust them a little because the time it's a little different rates. But overall this seems to be the same thing. Yay, and we just figured out what's happening. The high-level figured out what's happening in the second task, right? Now let's take a look at the third task. The third task, run because some timer fired. And by the way, you could see this little arrow that I have here. Arrow that's taking me to the place where the timer was installed. This is a very convenient DevTools feature, but for some reason by default it is off.

Capturing Errors in DevTools

Short description:

To capture a specific error in DevTools, enable the 'timeline event initiators' checkbox in the DevTool settings. This will display convenient errors that point to the code location when a timer is scheduled. However, there are some DevTools bugs that may affect the accuracy of the recording, such as unlabeled function calls and missing function captures. To work around these issues, you can try using an older version of Chrome, like Chrome 90. Comparing recordings can help identify these bugs and their impact on function calls and code execution.

So if you want to get this error in your DevTools, what you need to do is you need to go to DevTool settings, experiments, scroll to, oh yeah, actually don't scroll, but find the checkbox that says timeline event initiators and check it. So you need to have it checked. And then if you have it checked, what you would see is for every timer, for every request animation frame, call for stuff like that, you would see this very convenient errors that point you to the place where the code was, when the timer was scheduled.

But well, in this case I have a timer and this timer takes, the timer handler takes 90 milliseconds. And most of the codes here also seems like redux, some redux stuff, some function call, redux, redux, redux, redux, redux patch, rect redux, rect redux, keep going really low, rect redux, some anonymous function, name, rect DOM development, name TSX. Ooh, is there some expensive selector? Confusing.

So here by the way, here you could observe another DevTools bug that you would probably experience when you're profiling stuff. So whenever you get, like whenever I see this weird unlabeled function call in the middle of a main chart, so it's like a thing, right? In the middle of a frame with like a bunch of empty space underneath it, what actually seems to be happening here there is that DevTools failed to capture the actual function calls that are happening during this whole time. I raised the buck about this, and fortunately nobody fixed yet, but that happens with the Rheq profiling from time to time, this is very unfortunate, I, this is where I'm going to deal with because it's hide what's happening in the trace. And the only way to deal with this that they have is switching to an older Chrome version, which my reference is just Chrome 90, I typically use Chrome 90, and trying to record trace there because older versions are not, like I guess function differently, so like they're not always affected by the change.

So, let's actually try doing that because look, there's one yellow function call, unlabeled, like dot any function name here, and there's another yellow function call unlabeled without any associations here with a bunch of empty space underneath and some like random references. And this is just super, super weird. Like, or not super, super weird, it's like I saw this a bunch of times, so I know this is just debtos bug. So let's try to profile that part correctly. And to do that, I'm going to search for Chromium old versions, and I'm going to find any link that let me download Chromium old versions, which I was, oh yeah, I typically use this link. It looks very shady, but it actually links to like the official common data storage,, so it's okay. Frederick suggests to probably use the Firefox Profiler, which is actually a good idea. I have never tried it. It should probably work, but I'm not going to risk it now. So I'm just going to look for a game mac irm90 something. No, give me the 90, please, give me the 90. Why can't I see the 90? All right, whatever, I'll take the 90. 92, 92, 92. Yeah, this one. Let's take that one. I'm going to download. Should download Chromium. Open the archive. Oh, wait, no, no, no, oh no. Hold on. Right, because it's the. You need to right-click and, you need to remove the, some attributes. Hold on, what's the X-utters? I know you can see my column line history. Luts Chrome. Chrome Mac. Oh, Chrome Mac five. You can see I've done, this is not my first time doing this. From your map. And now it should supposedly open, yes. So this is Mac OS thingy, it just happens if you did a lot of tabs that's like unsigned or something, like not the official publishing channel or whatever. Anyway.

Mm. Now, if I take my URL and go to my old Chromium and open the app here and then try to record this same interaction using the old Chromium. So open. Open the dropdown, click record, click text, oh no, I did not enable CPU throttling. Stop recording. Hold on, let me do it with CPU throttling so we get the same picture. Right, open, record. Stop recording and stop. So what I'll see is I'll get, again, the same three. Wait, oh. Hold on. Next display. Ah. That's what I can get for having an extra display. I would get the same, I would see the same three things that I saw in the previous recording, right? And here's my timer fire that I was looking into. Oh no, it has the same bug. Dang, I really need Chrome 90. But like, anyway, I'm not gonna spend more time on this. This is something I typically work around by getting Chrome 90, maybe it's too old, I can't load it from there. But you could really see that it's, like once you start seeing it more often, you would clearly see that this is a Chrome bug. Because if you compare the two recordings, compare over this function calls, this weird unlabeled function calls are happening. You would just see that they're happening in very different places. You would say that most of the code looks the same. Right, we have the CarseB, it's split into two functions here. But this is another DevTools bug, it was actually fixed. Sometimes DevTools in the past used to, they are still, are they still doing it? I don't know. But sometimes when you would have a long function, a long single function, DevTools would split that function into two separate legs, which is also confusing. But if you look at the code, you would see that it's pretty much the same code, CarseB, yada, yada, yada, like all the same, the same execution stack, right? Except that here, this weird unlabeled function calls happens right after the next function. And here, the functions just continue.

Analyzing Expensive Renders and Components

Short description:

This part discusses a Chromium DevTools capturing bug and the process of analyzing the most expensive parts of the app's JavaScript execution and React rendering. The React Profiler is used to identify the most expensive components and re-renders. CPU throttling is enabled to simulate real-world conditions. The focus is on understanding the most expensive renders and components in the app.

So, what this tells me is that this is a Chromium DevTools capturing bug. Like, they failed to capture the functions for some pages. So, anyway, I'm just gonna, this is not the most expensive part. So, what I'm gonna do is I'm just gonna go back to my recording to my annotations. I'm just gonna mark that with, like, question marks. Like, question marks, probably mostly reducts, whatever's happening there I'm not fully sure about. Like this. All right.

So, now I have made a trace with the performance pain, and I have recorded everything that happens there. And I have analyzed everything that happens in the app on the high level, on the highest level on the higher level, and like got a high-level picture of what's happening in the app. So my next step is to figure out what's the most expensive part of what's happening in the app. So recording the interaction user DevTools. And now the interaction, is it mostly direct rendering? Is it mostly ReactFX? Is it mostly other JS or is it style or layout or comics? So if I look through this, I would see that this event handler is mostly just some other JS. This redux code is also some other JS. This is whatever it is, I don't know what's happening here. This could as well be also React code under the thing. I do not know for sure. These red things are the React rendering code and these green things are the ReactFX code. So the most expensive part out of all this seems to be the React rendering code. So I'm just gonna focus on it first. And let's see what we need to do next. So if it's React rendering, which we confirm by looking out for render root sync, next, we need to answer, the next question that we need to answer is what are the most expensive components or components that render most often and take most time? And to answer that, we're gonna use Rector Filer and just go, then go over all the recorded renders and take notes, take notes like this. So let's do just that. I'm switching back to the performance pane and I'm going to the profiler. And now what I'm going to do is, I'm going to record the same interaction using the, giving my dev tools. Here it is. Using the Rector Filer. So I'm going to click records. Okay, this is definitely DevTools bug. Look, it doesn't work even in proper chromium, not only in Arc. Why would they do this? Or like, I don't know. Sorry, it just has bugs all the time. It's sad, I'm sad. Anyway, I'm going to record the interaction we used on the recto filer. I'm going to look into what's happening in recto filer and oops, oops. Ignore what just happens. I'm going to see 19 re-renders that happens after just a single click on the single drop-down. Now, a re-render is not necessarily bad, if it's cheap, but some of these re-renders seems to be more expensive than others. I could see this by the height of these bars. So let's see what re-renders are the most expensive. So the first one took just 1.5 milliseconds. That's cheap. That's cheap. That's cheap. This one took quite a while. This took 14 milliseconds also without CPU throttling. Right? I don't have CPU throttling enabled, so the numbers may seem low, but they are worse for real world users. Then another re-render that took 7 milliseconds. Then cheap. Another 16.7, another 7. Anything else? Yeah, now all these remaining ones are just like one or a few milliseconds long. So just to get the picture that's closer to what real users have, I'm going to go back and enable CPU throttling and record this interaction. Hold on, I need to open the dropdown first. Record the interaction again. And so now I get 14 renders for whatever reason. Maybe some difference in the app codes, but I still get four expensive renders, which are now taking longer, and a bunch of cheap renders, which may or may not be significant, just some component rendering. So what I'm going to do now is I'm going to focus on the expensive renders first. Actually, no. I'm not going to focus on the expensive renders first. I'm going to start from the expensive renders. And I'm going to go over all the components that we have in the expensive renders first and then cheap renders, and try to take notes just in the same way as described here, finding the most expensive components. All right. So basically repeating the same thing we did here. On the previous step, we recorded, we figured out the most expensive parts of the JavaScript execution of the performance pane. And now we're going to figure out the most expensive parts of the Req Profiler pane. So let's do share stats. And one thing I like doing when I have a bunch of really cheap renders that are probably not really significant. One thing I like doing is I like going again to devtools settings and clicking on checkbooks height commits below 1 millisecond or 2 milliseconds or whatever. Let's set it to 1. Let's set it to 2.

Analyzing Expensive Renders in React Profiling

Short description:

In this render, the editor component took 7 milliseconds and the widget editor component took 44 milliseconds. The widget editor is expensive. Let's copy that node and write it here. This was in Chrome DevTools, and the next step is React profiler step two.

I don't know. Then we'll have like just 9 renders still visible. So what that does is that filters out that hides away the cheap renders. We'll get to them later. But for now, I just want to focus on the expensive ones. So let's see what's happening here. We have the first expensive render. What's happening in this render is we have the editor component taken 7 milliseconds with all its children, right? And the widget editor component taken 44 milliseconds, OK? That widget editor is really expensive. So I'm just going to copy that node and write it here. Right. So this was rest react, oh sorry, Chrome DevTools, and this is going to be React profiler step two. All right, so when the interaction is done optimizing.

Analyzing Component Renders and Optimization

Short description:

The Widgets Editor component rendered twice, taking a total of 84 milliseconds. The Property Pane View rendered twice, taking 56 milliseconds. The Table component rendered four times, taking 80 milliseconds. Other components, such as Positioned Container, were also rendered multiple times. The goal is to identify the most expensive components and optimize their rendering. In this case, we investigate the Widgets Editor component and its hooks. However, DevTools may report hooks that do not exist in the component due to limitations in Rack Profiler's visibility into custom hooks.

So when I click the text where you, where do you in the dropdown. So first I have the editor header rendering. So editor header rendered, if I click it, I could see that it rendered two times here and here. Editor header rendered two times. The first time took, so this time, 44 milliseconds, is not the time of the editor header, but it's the time of the whole render. So I need to actually click through this to see how long it took. So first it took 7 milliseconds, and the second time it took also 7 milliseconds. So it rendered two times, x 7 milliseconds each, which means it took 14 milliseconds in total. All right.

Next, the next component that got rendered was the widgets editor. Widgets editor also seems to have rendered two times. So widgets editor rendered two times. x, first time it was 44 milliseconds, second time it was 41 milliseconds. I'm just going to take the, let's say, 42. Rendered two times x 42 milliseconds each, which gives us 84 milliseconds. Then, so that was the first render, right? Now I'm done with the first render. Let's take a look at the second render. In the second render, I have the property pane view rendering and I have the table widgets, the table component re rendering. So the property pane view also rendered twice. Two times x 8, 29, 27, 28 milliseconds each, which gives us 56 milliseconds. And then the table component, ooh look, the table got rendered four times. Oh yeah, right. The table, for the first and the third time, is a part of this widget's editor render. So, I don't know, I'm just gonna write it down. I'm gonna write down all the renders because if we optimize the table, we'll optimize it in all four. So the widget's editor will also get, like this render will also get cheaper if we optimize the table. So I'm gonna sum up the all four renders. So let's see how long it takes. 20, 20, 20 and 20, right. Table rendered two times, x20ms each, oh sorry, four times, equals 80ms. That's an expensive component. And so, I just keep going through renders like this and I just keep finding the components that's rendered a lot of times, like cheap components rendered a lot of times or for example here you see the positioned container. Like, this is render number six. We have a bunch of positioned container instances, one, two, three, four, five, six. And each of these instances also are rendered four times, it appears to be. So like, if I click this instance, yeah, I could see four renders. If I click this instance, I could also see four renders, et cetera, et cetera. So again, this might also be significant. Positioned container. So there is five instances, five separate instances. Each of these instances seems to have rendered four times. Like, if I click any of those, I see four renders. Okay. The durations seem to be different though, right? Like, this takes two milliseconds in every render, but this takes 20 milliseconds in every, oh no, not in every render. So this is a bit harder to, this would need a bit more time to compute, but like, I don't know. If I estimate, like, what could it be? Well, I'm not gonna like go through all of them right now. I would typically go, but let's say like zero to 20MS equals 5.2020. So like zero to, what could it be in total? Like 60MS, like given the difference in stuff. So like roughly, right? We don't need to be precise. And so I just go through the recording like this and I collect all the components that are rendering. And I, once I'm done with this, I just look at the most expensive components here, which the far I have the widgets editor and the table, and they start trying to figure out how to solve this, how to prevent this from rendering.

So what we went through in the past hour was the very, like, basically a demo of the process of how I approach how you could approach debugging real complex real world issues in real world apps, taking the app, profiling it in Chrome Dev Tools, figuring out the most expensive parts and what they attribute to React Rendering, React Effects, other JS. Then if it's React Rendering, going to the React Profiler and finding the most expensive components. And then once you know the most expensive components, looking at these components in particular and trying to figure out why exactly they are rendering and how could we make them cheaper or how we could render them less often or render fewer of them or don't render them at all or maybe make them cheaper. So in this case, I'm going to take a look... Let's take a look at the Widgets Editor component, which took 84 milliseconds to render. So I'm going to go to Chrome and also exit the full screen view here. Go to Chrome and I'm going to get to the Widgets Editor components and see why it rendered. So if I click this component, if I switch between the renders, I see that the first time the Widgets Editor rendered because it's hook 5 and 47 changed. The second time it rendered because it's hook number five changed. And so at this point what I could do or what I might do is I might go to the code base and look at this widget and try to find this hooks, hooks number five and hook number 47 that was the Widgets Editor components. But if I look at the hooks this component has, I would realize that this component does not have 47 hooks. This component has hook, one, two, three, four, five, six, seven, eight, nine, 10, 11, 12, 13 and the so yeah, number of hooks. So the component has 13 hooks. Whereas DevTools tell me that hook number five and hook number 47 have changed. Does anybody know why this is happening? Why do DevTools tell me about hook number 47? What is hook number 47? Yes, so as Frederick says in the chat, hooks can have hooks. And indeed, as Rack Profiler, Rack Profiler does not have any idea about the custom hooks that you use. Rack Profiler only has visibility into Rack's own hooks. So for example, if you have some hook that says that const useMyHook equals like useState and then another useState.

Identifying Changing Hooks and Optimizing Renders

Short description:

Const useMyHook equals like useState and then another useState. Rack DevTools shows hook number one and hook number two changed. Two ways to identify changing hooks: switch to the components pane and look at the hook numbers or use the 'why did you render' tool. The tool tracks hook changes and renders, providing additional details. Enable the tool for the specific component to see why it rendered and which hooks changed. The logs show the changed hooks and the differences between objects. This interaction helps optimize app performance by identifying unnecessary renders.

Const useMyHook equals like useState and then another useState. And if all useMyHook and then at some point, like both of these states change, Rack will show you that hook number one and hook number two changed no matter where the, like disregarding the fact that this is the first, like for the widgets data component, this is the only hook that it calls. So Rack DevTools has no idea that we have this intermediate, the intermediary useMyHook function. The only thing Rack DevTools knows is the React's own hooks. So when you have this, how do you actually know which hooks are changing when you look at these and see the hook numbers? There are two ways to do it. The first way is to take the switch. Let's take this component, click this component and then switch to the components pane, which will it work now because the page reloaded yet probably won't work now. So I'm gonna re-record the trace because I'm making changes to the file and the page reloaded. I'm going to re-record the trace, open. Hold on, no. Open, record, close, stop. Widgets editor, hooks number five and hooks number 47 changed, same picture, right? So how do we learn which hooks this correspond to? So the first way is to switch to the components pane. So click this components in the profiler pane, switch to the components pane, which is going to select the same component of the components pane and then look through the hook section. Unlike the profiler pane, the components pane does now about custom hooks. So in this pane you would see all the custom hooks that you use in your app. And if you open, and you have this hooks, you would see the actual react hooks labeled with numbers, which you could use to match the, the hooks the profiler tells you about to the custom component hooks. So in this case, we had hook number five and hook number 47 changed. So let's see hook number five. Okay, no, this is not params, dispatch, store, redux, context, no, there's just contexts. Selector, redux, context, contexts, and select, aha. Hook number five seems to be the first used selector hook. Hook number 47 seems to be somewhere in the end here. Oh yeah, here is hook number 47 is a used effect inside dynamic app layout. Cool. So the first way to figure out why exactly the, or like what exactly this hooks, the first way I use is to switch the component plane and look at these numbers. That works, that's sometimes that's a bit noisy because you see it shows us that like the use effect function changed, which is not really a care about performance wise. That function could change as much as it wants. But so there's another way that I use sometimes. The second way that I use and that I really like is a tool called, why did you render? This tool, wow you see that they just made the header white. That just literally happened. It was not like this the day before. I'm amazed. So why did you render is the tool that just dropped into the chat. The is a tool that you add into your app like this and what it does is it's patches, reacts, overrides all the hooks and that's tracking codes for that looks whenever any components or any hook changes changes and causes something to render. So by default, like if you specify code like this, this app is going to try to track, this library is going to track all pure components and log for all pure components. The way I like using it is a little different. What I like using it for is like it's enabling for a specific component specifically. And here's how I'm gonna do this. So what I'm going to do is I'm going to enable why did you render for widgets editor to see the bunch of additional details about why we just saved it or rendered which hooks changed and how exactly this hooks changed. So here's how I'm going to do this. First I'm going to go to s3c slash index.JSX and I'm going to enable why did you render itself. I already have it configured also configured for the user selector hook for the custom one to lock about you selector specifically. And the second thing that I'm going to do is I'm going to go to widgets editor and I'm going to set widgets editor dot why did you render equals true. Or even better why did render locks on different values equals true? So just sitting true is going to warn you only about unnecessary renders when nothing changed. Lock on different values is going to tell you about every render that happened yet. So let's see what I'm going to have now. Now, if I like any time the widget's editor renders, I'm going to get locks into the console that will tell me why exactly this widget's editor rendered. Hold on let me filter out the Web Vitals locks. So in this case, you could see that when I clicked the input, the widget's editor rendered twice. And both of these re-renders happened because of hook changes. And it shows which hooks exactly have changed, which it was some use selector this may be the same use selector, this might be differently selectors it's kind of hard to tell from this, but it shows which concrete objects inside use selectors have changed. And it also shows the difference between these objects. So this so far is the interaction I do not care really about, so maybe I'll get to it later when I'm deeper into optimizing the app parts. But for now the only interaction that I care about is this one. And so I just click the drop down and I got three logs or two widgets edit or renders. I'm honestly like don't know like the concrete grouping logic that it uses like when exactly groups. So the number of logs here does not really correspond to the number of renders. But the reason that logs are what's like always something I'm looking at. And so if I look at these things, at the logs, I could see that the first change that happened, the first change that happened in the widgets editor is this use selector hook. And use selector return different objects that are equal by way. Which is a common performance in that pattern where you have a use selector that returns the same like the objects that will look the same, have the same fields, have the same data, have the same values, but the object references are not the same. So the object, so the objects are not triple equals. And so what it tells me already right away is that this some use selector return different objects that are equal by way. Then another use selector also return different objects that are equal by value. And then finally some third use selector, which may or may not be the same one. Also returned different objects that are equal by way. And I could look at the values. I could see like, okay, this is a use selector that has data that looks like this. And this is a use selector that has data that looks like this, which its name, which its name. And like same stuff here.

Debugging with 'why did you render'

Short description:

The 'why did you render' tool helps identify not only the hooks that changed, but also the values and how they changed. It provides insights into necessary and unnecessary changes. However, it does not replace the React Profiler, which shows which hooks changed but not how they changed. To determine which hook is changing, I use conditional breakpoints in the 'why did you render' library. This approach takes me directly to the code and shows the exact changes in the data. By making notes and analyzing the most expensive parts, I can focus on optimizing the selectors and improving app performance. It's important to consider the team's experience and the overhead of useCallback and useMemo when deciding whether to use them extensively.

Pref next, which thing, this will capture a pretty similar. Maybe this is the same use selector. So what I see thanks to why did you render is not only the, not only the hooks that got changed, but also the values that get changed and how exactly this values changed. And I could see right away if that change was necessary because like some actual fields changed or that change was unnecessary because the, like something rendered but the app state has not actually changed like only the object reference changed. And this is probably a big potential optimization candidate.

But now, like this is so far is like, this tool so far are not really, they compliment each other, but they like, why did you render does not replace the RectorFiler by default, right? Because if you look at the RectorFiler, RectorFiler, RectorFiler will show you that okay, hooks number five and 47 changed, but it will not show you how exactly they changed. If you look at why did you render, why did you render will tell you, okay, this is exactly how this hook has changed, but it will not tell you what, like which hooks exactly these are. And if I have multiple selectors, like here, it might be pretty hard to figure out what exactly these hooks are. So what I also like to do, to figure out to like, to just avoid switching to the profiler completely for some time and just work exclusively with why did render, what else we like to do sometimes to figure out which hook exactly is changing here, is I like to do the following. I would go to the line that looks any of these values, any of this hook your selector logs, I would click this line, which would take me to the source, which of the why did you render library that logs both the difference, right? I would right click the line that does the log, I would click select at conditional breakpoints, And I will get a conditional breakpoint that will only trigger when the hook that I'm interested in actually changes, which is going to be hook. I'm going to run hook name equals your selector. And maybe like, we need some other condition as well, because like all these three, all three of them are your selectors. Maybe how about widget name? Like the next object should have a widget name field. Because this one does not, this one is structured differently. So sources, can I find the pass string, prep value, next and next value. Yeah. So I can use edit breakpoint, your selector, and next where your widget name should exist. So now if I add this conditional breakpoint, the breakpoint that's only going to stop when the condition is true. And if I try to redo my interaction, the moment the selector that I was interested in changes, I would get these locks. You see these locks started happening, they were stopped in the middle of happening. But I would also get a breakpoint. And then if from that breakpoint, I look at the call stack, and I go several layers higher until I hit my own app code, I would get to the exact line, exact selector that selects the data and the exact selector that gets changed with the 100% precision, unless source maps are off or something. And so this is the approach that I really, really like because it does not require me counting the numbers like in the components pane, it takes me straight to the codes, and also it shows what the exact data, the exact changes that the data comes through. So I see the hooks that are changing and I see how exactly they're changing, and that's really, really helps in debugging. And so, what I would keep doing in this case is I would keep... I would go back to my flowchart and I would keep making notes. So let's like, we're debugging the widgets editor, right? So the widget editor renders because hooks number five and 47 changed, whatever these hooks are. So profiler tells that hooks number five and 47 changed for the first time, right? And then just hook number five changed in the second render. Hmm, so like first render hook number five and 47, right, second render just hook number five. And what why did you render tells me is that the first hook that got changed was the widgetsUseSelector. Yes, it was the widgets use selector. Okay. And that was an unnecessary change because the objects stayed the same. Plus the hook number seven was some other use selector, which I would debug it. And then the third selector was also some other use selector, maybe the same use selector, because look, hook number five, hook number five. And the data shape of the first hook and the third hook is really similar. Maybe they're the same hook, I could confirm that by setting the breakpoints or I could just assume, and then just verify that later, plus the widgets use selector, which was also unnecessary. But it's like with a question mark because I have not verified it by setting the breakpoints yet. And so I make these notes and I figure out, so I just figured out where the components is for rendering. And then like from here on, I would just go to and do some, like figure out why these selectors are changing and maybe do some selector specific optimizations, some redux optimizations. This is not, again, like it's not the subject of this workshop, but yeah, I don't know. Maybe I could optimize the selector. Maybe I could add some deeper quality here, pass the second parameter to your selector. Maybe I could refactor the selector. So stuff like that. But that's how you approach, that's how I actually approach debugging a large complex real world app. I take a lot of notes and also I go from the top to bottom figuring out the most expensive parts. But with notes and with knowing the most expensive parts, I'm typically able to focus my efforts on the right parts and bring some real changes. So I see some questions in the chats. Lucas asks, is there anything else that widgets editor that why did you render true to make this work, I can't make it work for some reason. So the change that I made was actually widgets editor, why did you render lock on different values true? Which makes it lock for every re-render. But, oh, sorry. I think why it doesn't work for you if you're following the changes from the app, why it probably doesn't work for you, have you uncommented this line in index.js? Because you also need to- No, that'll be it. Thanks. Awesome. Great. All right. Any other questions folks about what we went through so far? All right. I'm seeing some questions in the chats. On a daily basis, would you use useCallback, UseMemo everywhere pre-optimization? Ooh. That's a great question. And I do not have like a yes or no answer. I think it mostly depends on the team I work with. So ideally if like all the engineers I'm working with are like super experienced with React and like know very well, like where they need to optimize, I would not do that. For two reasons. First is useCallback, UseMemo is an overhead, a CPU overhead. Like every time you need to, every time like the component renders useCallback and UseMemo have to compare the dependencies, right? And that's cheap, but that's like at its app. And by the dependencies, right, I'm just talking about... Give me something. Give me something.

Optimizing React Code and Effects

Short description:

In a senior team, it is advisable to avoid using useCallback and useMemo by default due to their performance costs and increased memory usage. However, in a mid-level team, using useCallback and useMemo as a rule of thumb can bring performance wins. The useWhyDidYouUpdate hook is a lightweight plugin solution that helps identify which values are changing in a component. It provides insights into unnecessary renders and can be a useful tool for optimization. When optimizing React renders, tools like the React Profiler, useWhyDidYouUpdate, and conditional breakpoints can help identify and address performance issues. The effects branch of the FlameChart focuses on optimizing React code that runs effects, rather than render-related code. This requires different tools and techniques to identify and solve performance problems. For example, analyzing frame rendering stats can help identify laggy interactions in an app.

So like this, right? Every time this component renders useCallback has to compare these two properties and with a bunch of useCallbacks there are UseMemos in the app. So there's the first reason. The second reason is that they also as the, like increases the memory usage. Like a friend of mine's actually going to do, talk about this on React Day Berlin, if his talk is accepted. But I very much hope the stock is accepted because he's going to talk about the memory usage, like how much memory useMemo actually uses. But the idea is, if you, to compare the previous values with the next values, React has to store the previous way of summer. And they increased that increases the apps memory usage. The garbage collector cannot free these values even if they're not really necessary anymore. So my question is like in it, in a very senior team, I would probably avoid using a use callback and useMemo by default. Because that's gonna, that's not free. Ooh, wow. Is that an actual link? Wow. That is an actual link. I should bribe somebody at GitHub to recommend my content. But this actually, this actually a good article about, I told before this actually good article about the performance costs of use callback and use memo. So let me drop that into the chat. Okay, no, this is going to take my job at some point, definitely. But in a like mid-level team, I would probably do use use callback and use memo by default simply because like if people are not like deeply experienced with RECT, then using use callback and use memo just as a rule of thumb, like you don't need to think about it, you just apply it by default is probably going to bring more performance wins than the costs it would cost. So I think it depends on the level of team.

All right, Frederick asks is, why did you render affecting RECT score performance on production since it seems to extend by attract? Yeah, it very much does. I have not measured the exact changes, but I think I saw like, you know when you like measuring stuff without what you to render, then adding what did you render and then measuring it again, I think it's sort of getting like 30 or 50% slower. So you never want to enable it in production, you only want to enable it in development and the like example code actually takes care of this. Frederick asks, I sometimes used a hooks, why did you update any thoughts on this? Yes, so I'm going to show this. No, I don't think I'm going to show this, we're not going to have enough time for this but yes, so there's a great hook called use ydtupdate. Use ydtupdate, it exists in several libraries, including a hooks, I really liked the version of it that's copy pasteable. So like they use hook website used to have this function until the recent redesign, now they don't for whatever reason, it just redirects to some other function that's completely different than you know. But I saved it into a gist and I'm going to share the slides afterwards. So these links are going to be clickable. And if you click this use ydtupdate link, you'll get a gist, that's basically a copy pasteable version of the use ydtupdate hook. And this hook, what this hook does is it just tells you locks into the console and some props you pass into it or some build is to pass into it, change. And I like using this as a really lightweight plugin solution because I really like that it's copy pasteable, like I don't need to install anything. I don't need to restart my server or anything. I could just copy the hook. Paste it into the components and then use it to tell me which values are exactly changing. Hold on, let me just make a TypeScript compatible. Yep. Well a really bad type still. So let's see how it could help here. So remember how we were trying to figure out which of these hooks were changing, right, and we were like adding breakpoints and just like we were looking, sorry, adding breakpoints and like we were looking at the values. We were looking at the call state to figure out which choose selectors are actually changing. Another way to figure out which hooks are changing is to just use this, use why did you update function, which you can find by Googling or you'll find it in my slides, in this slide that I'll share in Discord. So the way you just wait for you to update works is you call the hook, you pass in the component name or basically any string that you want. So I'm going to call this, which it's editor selectors. And then you pass in the videos that you want to observe. So canonically, it's typically the prop subject, and monically the way you will use this, you'll pass the component name and you'll pass the props and the function will log you when any of the props change. But I like you to use it for non-canonical purposes because with, for Props I can already use DevTools, but for figuring out which selector has changed, well, none of the DevTools, none of the tools are good for this. So in this case, to figure out which selector has changed, I could call UseWhiteD2Update, pass any string that would just be locked into the console and pass the values that I want to observe. And then every time any of these values changes or like, sorry, anything of the values I passed into the component changes, I would get the lock of which value exactly has changed and also what was the previous value and what was the next value? And that's like maybe the easiest, the most lightweight way to figure out which of her hooks, which of the values in your component have actually changed. Here I could like see right away that, okay, I passed five selectors, one selector changed from this value to this value, this means I should focus on this selector and try to optimize it. So that's, thanks. That's a great question. And yeah, I really like using this tool, it's great. All right, any other questions about what we went through so far before we take a look at the effects branch of the FlameChart? Let's take a look at the effects. So everything we went through so far was mostly focused on React renders because, typically, often, that's the most expensive part. And so you need to apply a bunch of tools, a bunch of techniques to optimize this. So what we went through so far is we took the simple app first and then we took the real world app later, right? And we recorded the interaction using DevTools. We analyzed every part of the DevTools recording. We saw that most of the time is spent in the React render. So we switched to the React profiler and we recorded the trace with the React profiler and we figured out the most expensive components. And then we tried to answer why these components were rendering. And we used the record-away-components-render checkbox. In DevTools, we used ydToRender, we used use-by-data update. These are all the tools that are regularly used to solve issues. But what happens, if you make a recording and instead of render-routing being the most expensive part of the code, it's some React code, but the React code that runs effects. Let's take a look. Here's another interaction that is slow. And we noticed that it's slow maybe because the users complained about it, maybe because we noticed. But, let's say the users complained that whenever they're opening the widget properties of any widget, the app lags for a bit. And again, you would not be able to see this because I'm screen sharing, but so I'm going to enable frame rendering stats and you could use frame rendering stats to see when the app lags. So, so far the app is pretty smooth, I'm just hovering instead.

Analyzing App Performance and Function Execution

Short description:

To debug the app's slow performance, start by recording the interaction using DevTools. Look for spikes in CPU activity, zoom in on the relevant spike, and analyze the functions being executed. Annotate the different parts of the recording to identify expensive renders and effects. Pay attention to the time spent in the commit layout effects function and the recalculating stacks process. Although style recalls are out of scope for this workshop, there are resources available to learn more about them. The second part of the analysis focuses on another RectRender, the commit layout effects, flash-passive effects, and run microtasks. Annotate these parts to understand the time spent in renders and effects. Use the annotations to guide further profiling and debugging of the app's performance issues.

But now if I open any, if I click any components, which also opens its widgets pane, you would see how the app just get frozen for like a really long period of time, right? Look, that's again, like that was a really thick bar of threads here again. So this means the app was pretty slow. How do we debug this? To debug this, the first step that I'm always taking is I'm taking the interaction and recording the direction using DevTools. So I close the dropdown and I open the dropdown or open the properties pane, whatever, and I click stop recording. And then I look at the recording, I see two spikes. The first thing I look at is the CPU row and I see two spikes of CPU activity, one when I close the dropdown and the second one when I open the dropdown. And this is the spike I care about so I'm going to zoom into it. And if I zoom, if I look into the spike and if I keep going over the main recording, analyzing it in the same way we were analyzing it here, I would notice that most of the time here is spent running the commit layout effects function. So let's see, let's actually do the same scrinching thing shall we? What's like quicker in this case. So I took a screenshot, I'm going to create a new rectangle or whatever. No, please click, click, no, no, I ruined everything. Right. I'm not going to create a rectangle. I'm going to paste the screenshots and just go and annotate the parts of it. So let's do that quickly, shall we? The first part 34 milliseconds, Watch Sync Callbacks, PerformSynchronicRouteRoot, RenderRootSync. We get some React rendering that takes 30 milliseconds. It's also the funny bits, which we are not going over in this workshop, is that most of the time here further below is spent recalculating stacks. So we have some expensive components or we have some expensive render. But most of the time here it spends not running JavaScript, but running browser-specific calculation code that's out of the scope for this workshop. So I'm just going to oops, highlight this like this and no fill and say like it should be violets. Let's make it violets and say that, okay, this is the React render, but with like most of its time spent recalculating, running a style recall. And I know that is the style recall because I see that violet rectangle right here and also this rectangle says recalculate style. So like, okay, it's a style recall. And as I mentioned, this is out of the scope for this workshop, but there is a branch here for this with like the first link could start from to learn more about this, showing all the different styles that you can run in the style. Showing all the, like telling more, telling you more about this issue and showing all the properties that go with this. So if you encounter this, hopefully this helps or you could also come to the big workshop I'm running soon and learn. Oh, actually, no, sorry, we're not going to cover that there. So don't come there for style recalls. We're not going to cover that there. So that's the first part.

The second part, let's see, we get another RectRender, which takes 74 milliseconds. So I'm going to highlight that with green. So this is RecTrender. I'm just going to use the same colors and not put any labels. Then we get the commit layout effects that takes 600 milliseconds. And it was red. Then we get some flash-passive effects, more effects. So I'm going to just extend this a little. Can I make it? No, OK, it can only move one way. It anchors to the grid. And we get another React render. Render root sync 64 milliseconds. So, OK, this is green. Most of it here is green. Then we get some flash-passive effects, some minor effects. I'm not even going to load them. I'm not doing at this point because they're just seven milliseconds. And then we get some run microtasks. Run microtasks thingy. And, oh, the same picture that we saw during the previous profiling session. Do you recognize this? Render root sync commit root. Render root sync commit root. Render effect. Render effect. The same stuff that we saw over here. So the same thing is happening there. So I'm just going to try to copy these. And no, OK, no, this tool is really not convenient for annotations. I'm just going to label this as renders and effects. And it's going to be green because it's mostly renders. So once I annotate all this, I'm going to notice that most of the time here is being spent in effect. And so I'm going to go to the flame shirt. And I'm going to go to the name, to the next rectangle, which tells me how to profile this next. But the annotating bit is really, really important. Because if I don't do the annotation, if I don't look through the React function names, if I don't figure out if it's a render or an effect pass, what I can do, what could happen is I would look at this code. I would notice that most stuff that happens here is a React render. And so I would switch to the profiler. I would run a React render which does not want to run. Spew fork slowdown profiler. I would run.

Analyzing Effects and Optimizing Performance

Short description:

When analyzing the effects in the React profiler, it's important to note that it only shows renders and does not display the effects or their cost. To optimize the effects, it's necessary to go further down in the trace and examine the actual effects called by React. By analyzing the effects, it's possible to identify which ones are being called and why they are expensive. In the example given, there are eight effects, four of which belong to styled components. Since styled components work differently in development and production, they are not a concern for optimization. Instead, focus on the effects that are being run and understand what is happening within them. In the case of the component did mount effects, it is not possible to make them run less often. However, it is worth examining the code within the effects to identify any potential optimizations. By analyzing the code, it may be possible to make the effects cheaper and improve overall performance.

So I would notice that most of the code in the trace is the React render. Oh, sorry. I would notice that what might happen, if I don't do that annotation, is I would notice that most of the codes in the trace is React. So I would switch to the React profiler, and I would record a trace. And I would look into the trace. And I would see some expensive render, and I would try to optimize this render. I would see that, OK, there's this component expensive, and there is this component expensive. But I might totally miss that the render here takes just 43 milliseconds. And whereas the effects take literally 10 times longer. React, React DevTools now show this for React 18 apps. But if you still have React 17, you're not going to see this hint at all. So you could completely miss this. And React profiler does not show the effects at all. What I see here is I only see the renders that have happened. I do not see any of the effects. And I do not see what exactly was expensive. So if I only focus on the React profiler, I'm going to be wrong because I'm going to be to optimize the wrong thing, which is why we do this whole highlighting thing.

So what do I do? What do I do once I've highlighted and once I figured out that the most significant chunk of the code is spent running React Effects? I follow along and I try to figure out what are the most expensive effects? And then I try to figure out, why do these effects rerun? And then I've tried to figure out, how can I call them less often or make them cheaper? So let's see the effect set that we are running. I click record, I click this component which opens its property pane. I zoom into the click event. Ooh, I get the function callback again. Ooh, I get the function callback again, which hides, obscures some of the stuff that's happening here. This is very annoying. Let me rerecord that. And also pin this to the right. So it records. Open, stop. OK, yeah, now it's gone. So I get this 614 millisecond effect. And so now I cannot use direct profiler anymore. What I need to do is I need to actually go farther down in the trace, go all the way down until the React code ends. And I need to look at the actual effects that react calls and try to see which of them are getting calls and which of them are expensive and why are they so expensive. So let's do this here quickly. We have the commitLayoutFX call. We that calls commitLayoutFX begin something complete, commitLayoutFX on Fibre. That's all React DOM development functions. And then, OK, I get more React DOM development. Then, at some point, I start getting functions from different components, from different files, not from React but either from styled components, which is this anonymous function, or component didMount from myindex.s6 from Kot Editor slash index.s6. Again, styled components, component didMount from Kot Editor index.t6. Styled components, component didMount. Styled components, component didMount. All right. So this is pretty interesting. We have eight effects. Four of them belong to styled components. So I'm not sure if I can optimize this right now. So me being the profiling person, I'm saying to myself, I am not sure that I can optimize this right now. But me being the workshop person, I'm going to tell you that what you see here is actually, you don't need to worry about this because styled components work very differently between development and production. And to production, they're going to be much faster. So we are not going to have this bottleneck. But coming back to me, the profiling persona, I'm going to ignore styled components for now, and I'm going to look at the effects that I'm running and just try to figure out what's happening here. So I see that I have 4 component did mounts from the same component, which I guess means we have 4 instances of the components. If I click this, I could see all the code that happens here. We are creating some options. We are creating and we're calling CodeMiro from TextEditor. We're creating a code editor. We are adding a bunch of event callbacks. Then we're calling getInputValue. We're setting the value. We're calling updateMarkings, and we're calling startToToComplete. So at this point, I'm going to ask myself one of two questions. First, can I make this effect run less often? Or can I make it cheaper? Either of those is going to help me to optimize this component. So first, can I make this effect run less often? I don't think I can because it's a component that mounts. It always gets triggered whenever the component mounts. So I cannot really do anything with it. So no, I can't. But can I make it cheaper? So to answer this, I'm just going to go back to DevTools and I'm going to look through everything that happens during the component de-mount call. And I'm going to try to figure out how to optimize that. So in this case, I can see that the front exterior call takes more than half of the total cost of the whole cost. But it's a Codemirror code.

Using the ConsoleTime API for Profiling

Short description:

The ConsoleTime API is a built-in browser API that measures the duration of code. By wrapping specific parts of the code with console time and console time end calls, we can measure their durations. This lightweight approach provides insights into the performance of different code sections. The ConsoleTime API is useful for quickly profiling code and identifying expensive parts. It can be used alongside the performance pane to record traces and analyze the durations of annotated code sections. This tool is especially helpful when dealing with large apps and trying to locate specific code sections in the recording.

So again, it comes from the third-party library. And maybe we can optimize it. Maybe we can't. I get a couple other Codemirror calls. I get this start of the complete call, which takes around a quarter of time. And it is our code. And so at this point, we're gradually getting from the debugging area to the solving area. Right? So we got the concrete. We found the concrete bottleneck. And we were trying to figure out how to optimize it.

And so at this point, I'm not going to go further. But I want to show one last tool that is often very useful for profiling that I sometimes use instead of just looking into DevTools. This tool is the ConsoleTime, ConsoleTimeAnd API. And by the way, I'm curious, folks, for folks who are here, have you heard of this API before? Have you used this API before? Could you send a plus into the chat if you used ConsoleTime before or minus if you haven't? Plus, plus, plus, plus. Minus. Yes. Uh-huh. All right. So some folks can use, some folks haven't, so I'm going to show it. All right.

So the ConsoleTime API is a really nice built-in browser API that lets you measure the duration of any piece of code that I want to profile. So let's say I wanted to. I have this very expensive component that's not right in the code editor. Indexed as six component at mine. And so, like, let's say I was just going over this and I was trying to figure out which part of this is most expensive. I could do this by recording a performance trace. But it's not always convenient, like it's a lot of efforts, it makes the app slower. I don't know, sometimes I just want to profile something quickly. Also, I might have a bunch of anonymous functions like these that are hard to attribute, right?

So, one way to figure out why component mode is expensive is to look through dev tools, but another way to figure out why it's so expensive is just to split it into several parts and wrap this parts with calls to console time and console time end. So, let's try doing that. So, I'm going to split this into several parts and I'm going to wrap this parts with console time and console time end calls passing the same label every time. So, the first part is going to be the part where we create the options. Console time, console time, end. The second part is going to be could mirror dot from text area. Console time, console time ends. The third part is going to be could mirror event listeners. Console time, console time ends. And the fourth part is going to be, the first part is going to be could mirror dot set calls. Console time and console time ends. And let's do the couple last ones. Please update markings. And they start to complete. So what this is going to do is this will ask the browser to measure every duration between console time and console time end calls and log the duration with the label that it pass into this console calls. And that's unlike the performance recording. That's very, very lightweight. So I can easily run it without like dispatching full browser, maybe I can even keep it in production. Actually, I don't know. Can I keep it in production? Maybe I can, if it's like not performance critical code. So let's see how that looks. I'm going to open the performance. Sorry, open the widgets pane. And I'm going to look at the logs. And in the logs, I would right away get the direction, get the durations of every part of the function that they've wrapped with console time. So you can see that this part was cheap. This part was cheap. These four of these parts were cheap. Whereas these parts, these two parts were expensive. So I could probably now repeat the same thing with, oh, sorry, these two parts were expensive. I could probably now go into this start after complete and like add console time and console time here now and like figure out, try to figure out which parts of these functions are expensive and then just go blabber and blabber like that. So I could use that. Or another way I could use this if I have like a lot of stuff in the performance pane and it's hard for me to find where exactly these calls are happening or where exactly, where exactly component did mount is happening. I could also add this component did mount calls. Sorry. I could also add this console time calls and then go to the performance pane and record the trace with the console time calls in the codes. And what will happen then is I would get the timings area that shows me all the parts of the code that were annotated with either the performance mark and performance measure API or the console time and console time end API. And so if for me, like if I know this component did mount happens multiple times through the recording and I want to make it easier for me to find this component did mount calls without like scrolling through the whole recording and like trying to figure out where they actually are what it can do is I can just add the console time calls or performance mark, performance measure calls and I would get separate annotations in the dev tools showing me when exactly these calls happened. And this is convenient to use as well in profiling large apps when you want to find something really quickly in the full recording. So this is the FX bit. With FX, the most important bit about FX that with FX, the profiler does not help. The profiler does not show you anything about the most expensive effects.

Profiling Effects and Q&A

Short description:

You have to use the dev tools to find the expensive functions and make them less expensive or run them less often. Use UseYD ToUpdate, console time and console time end APIs, and Performance Measure and Performance Mark APIs to identify and optimize the most expensive parts of effects. We covered profiling a simple React app, solving a typing issue, using tools like why did you render? and React DevTools. For effects, use DevTools and utilize console time and gate to highlight recording parts. Now it's Q&A time. Feel free to ask any questions or rate your understanding from 0 to 10.

You have to use the dev tools to find the expensive functions to figure out where these functions are expensive and to figure out how to make these functions less expensive or run them less often. And to do that, you could use UseYD ToUpdate, and you could use the console time and console time end APIs, and you could use the Performance Measure and Performance Mark APIs to figure out why the effects run and which parts of the effects are the most expensive.

This is the effects. This is how you approach profiling effects. And this is pretty much it for the workshop material today. I'm wondering, folks, if you have any questions about everything we went through, about the stuff we went through at this point. Just to reiterate what we went through was we started with profiling a simple React app. We looked at a single problem issue, which is typing to the editor. We figured it out what makes it expensive using React profiler. And we've solved it. Then we looked at the large real-world app, and we used more tools. We used the why did you render? We used to use yd2update functions. We saw how to set a break point with yd2render to figure out which hooks exactly changes. And we saw how we used React DevTools to figure out which hooks exactly change. And with React Effects, we learned that you can't use the React profiler for effects. Instead, you need to use DevTools. And when you're using DevTools, some things that help is the console time, and the console time and the gate, which helps to highlight the parts for the recording.

So this is the Q&A time. Hit me up with any questions about everything we went through so far, or hit me up with any other performance questions in general, overall. And I'll be happy to answer anything. We have 15 more minutes left. But also, just let me know, just to let me understand, could you drop a number from 0 to 10 into the chat? Not the rating, but to show me how well you feel you have a grasp of how clear was everything we went through so far? Like, where 0 is like, everything was extremely clear, 10 was like, everything was extremely, really clearly, 100% of it. So from 0 to 10, a number in the chat that shows how well you feel you get a grasp of everything we went through today. I see a bunch of 9s, 8s, 8s, 8s, all right. Yeah, folks, with 8s or also with 9s, if you have any concrete questions, feel free to hit me. And I'm going to answer the Mayansk question about any suggestions about state manager updates like Redux or Coil.

Optimizing State Manager and Memory Performance

Short description:

Optimizations for the state manager include using Reselect to prevent selector re-rendering and the use of the useSelector hook with a custom equality comparison function. Migrating from Redux to React context can be challenging as it requires reimplementing Redux's performance-oriented primitives. Memory performance can be analyzed using tools like MemLab, which provides custom interfaces to measure component memory usage. Thank you for attending the workshop.

So are you asking about optimizations for the state manager, like how to solve this? Yes. So kind of yes. Like, I would actually. Sorry, am I talking? So there is going to be a bigger workshop that I'm running like in the end of the month which also talks about solutions. I don't think we do Redux here. Oh, no, we do Redux here. Yeah, we are going to do Redux here. Like, there's a time limit. So I have to figure out what to cut. OK, so we are going to do Redux optimizations there. So to give a really quick answer, like there's a bunch of stuff that I use. Sorry, can I give a quick answer? Like, if you're looking for some, sorry, I probably can't give a quick answer on the top of my mind right now. But it's like we have like 10 solutions in that workshop. One of them that I remember right away, right at the top of my mind is Resellect, which is a library that helps to optimize some, to prevent some components from, sorry, to prevent some selectors from re-rendering. I don't use it that often. Actually, it's not like the first solution I go for. But I've seen a lot of apps using it, it's helpful. Anything else? But no, like most of the stuff that we go through in that workshop is mostly like refactoring. Like this is how like we write those selectors. So that's kind of slower to show. Another thing that you can use sometimes, also just to answer your question, another thing that you could use sometimes is, use selector. So the use selector hook has a different second, sorry, a second parameter, which lets you specify the equality comparison function. Which like by default, what Redux does, it's just triple equals the old value and the new value, right? And if the reference is different, then you'll get a re-render. But you can overwrite that by passing the custom equality function. And again, that's more of an escape hitch because if you pass like deep equal here and your objects are really big, that's gonna be slower than a re-render. Like I saw an app that's like a deep equality was taking 500 milliseconds or something like that just over two objects, that was a lot. But in some concrete cases, that's a good escape hitch to use to avoid re-rendering the component because it's like to change because like maybe the reference changed, but the ID, the IDs didn't change the produced ID. You could compare IDs instead of doing triple equals. So it's stuff like that that's regarding Redux. I don't have any recoil suggestions I'm afraid.

Alright, do do do do do do. Nir asks if we can get through the GitHub link. Yes, so the documents that we're working on is here and the GitHub link is here. And I'm also going to drop all the links into the Discord chat. So which is here, we could find it in RS workshops. More questions, Frederick says I learned to distract my apps performance when migrating to the new fancy app context at time. Now everything is rendering almost in every update, I want to use the Zeus stunt in the future. Ooh well, sorry, sorry that happened. I do not have any experience with the Zeus stunt though, I hope it helps in the future. But actually, hold on. I'm actually curious, hold on, I'm surprised you migrated like from Redux to just the React context. Was that it or did you do something else? Yeah, kind of, it was very old app. The old Redux code, it was pretty big, these reducers and this state stuffs and so on. Yeah, so I thought, okay, cool, we have the app context, the React context now, let's make providers for this, but wasn't actually a good idea. Mm-mm, yeah, I see. Yeah, I guess... I don't know, one thing about... Redux does not use the context under the hood, right? It uses a different mechanism, but it's not that complex on its own. So one thing that's good about Redux, it just gives you some primitives that make it easier to be... Well, not super easy, but easier to be performance by default. And so when you switch to, from Redux to context, you basically have to reimplement this primitive, sort of figure out the different primitives from scratch. And I guess that's sometimes... I would not trust myself to do a good job at this. So, yeah, I guess. It's real unfortunate that that happened. I see how that could happen. I mean, this all Redux code didn't use reselect or something like this. So I don't know if I made it worse, but it was bad now. Yeah, and I guess two standard or having different stores like this should solve it. Yeah, well, I hope it helps. Paulo asks about memory performance. Yeah, I have no experience with that, sorry. I know one tool, MemLab, which is, so like debugging fronts in dev tools is it like, I'm gonna say it's, sorry, it sucks. It's like dev tools, memory tools are very hard to use. So like all the people that they saw like seriously doing memory analysis, they were typically using MemLab instead. And also I have recently learned, again, with the same friend who is going to give a talk at track day Berlin, that he like, his talk is going to be about custom reporters for the MemLab. So you could wrap, basically custom interfaces on top of MemLab and use that to like show how much time, sorry, how much memory every component uses, for example, how much memory like use memory uses. And so this is like if I were to optimize the memory, I would start from this. Thanks so much for coming through this workshop. Was very happy to present to you and talk to you. It was a pleasure, thank you. I hope to see you the next time. Yeah, Q&A until I slide.

Watch more workshops on topic

React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Featured WorkshopFree
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?
There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.
Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.
You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.

React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.
The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.
React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.

React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.

React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents
- The different kinds of React application tests, and where component tests fit in
- A mental model for thinking about the inputs and outputs of the components you test
- Options for selecting DOM elements to verify and interact with them
- The value of mocks and why they shouldn’t be avoided
- The challenges with asynchrony in RTL tests and how to handle them
- Familiarity with building applications with React
- Basic experience writing automated tests with Jest or another unit testing framework
- You do not need any experience with React Testing Library
- Machine setup: Node LTS, Yarn
React Summit 2020React Summit 2020
96 min
Rethinking Server State with React Query
Featured Workshop
The distinction between server state and client state in our applications might be a new concept for some, but it is very important to understand when delivering a top-notch user experience. Server state comes with unique problems that often sneak into our applications surprise like:
- Sharing Data across apps
- Caching
- Deduping Requests
- Background Updates
- Managing “Stale” Data
- Pagination
Incremental fetching
- Memory
Garbage Collection
- Optimistic Updates
Traditional “Global State” managers pretend these challenges don’t exist and this ultimately results in developers building their own on-the-fly attempts to mitigate them.
In this workshop, we will build an application that exposes these issues, allows us to understand them better, and finally turn them from challenges into features using a library designed for managing server-state called React Query.
By the end of the workshop, you will have a better understanding of server state, client state, syncing asynchronous data (mouthful, I know), and React Query.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Advanced Conference 2021React Advanced Conference 2021
39 min
Don't Solve Problems, Eliminate Them
Humans are natural problem solvers and we're good enough at it that we've survived over the centuries and become the dominant species of the planet. Because we're so good at it, we sometimes become problem seekers too–looking for problems we can solve. Those who most successfully accomplish their goals are the problem eliminators. Let's talk about the distinction between solving and eliminating problems with examples from inside and outside the coding world.

React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
JSNation 2023JSNation 2023
29 min
Modern Web Debugging
Few developers enjoy debugging, and debugging can be complex for modern web apps because of the multiple frameworks, languages, and libraries used. But, developer tools have come a long way in making the process easier. In this talk, Jecelyn will dig into the modern state of debugging, improvements in DevTools, and how you can use them to reliably debug your apps.
React Summit 2023React Summit 2023
32 min
Speeding Up Your React App With Less JavaScript
Too much JavaScript is getting you down? New frameworks promising no JavaScript look interesting, but you have an existing React application to maintain. What if Qwik React is your answer for faster applications startup and better user experience? Qwik React allows you to easily turn your React application into a collection of islands, which can be SSRed and delayed hydrated, and in some instances, hydration skipped altogether. And all of this in an incremental way without a rewrite.