In the first of this two-part series, Mark Rickert joins the podcast to talk about performance-tuning your React Native apps.
In the first of this two-part series, Mark Rickert joins the podcast to talk about performance-tuning your React Native apps.
This episode brought to you by Infinite Red! Infinite Red is a premier React Native design and development agency located in the USA. With five years of React Native experience and deep roots in the React Native community (hosts of Chain React and the React Native Newsletter), Infinite Red is the best choice for your next React Native app.
Helpful Links:
Connect With Us!
Todd Werth:
Welcome back to React Native Radio Podcast. Brought to you by Comcast Cable. Comcast, why the rush? Relax, enjoy the extra break. Episode 217, Performance Enhancing Drugs for your React Native App, part one.
Jamon Holmgren:
So did either of you order the new MacBook, the one with the M1 Max CPU?
Mark Rickert:
I did, and I'm getting it in like two weeks. I'm really excited.
Jamon Holmgren:
In two weeks. How'd you pull that off? I ordered it and it's not coming until December.
Mark Rickert:
Because I ordered it immediately when it was released. I was on the store and I ordered it and I'm like, "This is my new Mac." I've been struggling along with this five-year-old MacBook Pro i9. And it's not enough for me anymore. So I'm very excited about getting it.
Jamon Holmgren:
Struggling with an i9. Yeah, that's where we are right now. I actually, I deliberated for longer. Besides I was having problems loading the store. It was timing out and stuff. So I waited a whole day. And by that time it had stretched out into either mid-November to December basically. You could pay extra eight dollars to get it one day earlier, but it was still a two-week span.
Jamon Holmgren:
I have a 2019 MacBook Pro and it has an i9 and it's like, this is a very cool computer. I love it. It's great. It has a touch bar. It has touch ID-
Jon Major Condon:
Rest in peace.
Jamon Holmgren:
... Yes, rest in peace, exactly. That touch bar is no more. I had to get this though, because I'm doing streaming now as you all know, twitch.tv/jamonholmgren, little plug there, and I can't open Android Studio and Xcode at the same time. It just bogs down the computer. I can't stream at the same time. It is doing all that. So yeah, I ordered it. I'm really excited, can't wait for it to come, and you're going to have to tell me how awesome it is, Mark, so I can just be extremely envious.
Mark Rickert:
Yeah, it's going to be a huge upgrade for me. Five years has been what I usually keep my computers for, but we're getting a little long in the tooth, so I'm very excited for the extra speed boost of the new M1 Max chip.
Jamon Holmgren:
Speaking of speed boost, that is our topic for today. We're going to be talking about performance. But before I do that, I'm going to go ahead and introduce who I'm talking to here. Of course, I'm Jamon Holmgren. I am the host of React Native Radio, co-founder and CTO of Infinite Red. And, I don't know, what else? I guess I'm a software developer of 16 years professionally. I've been doing this since I was 12. So yeah.
Jamon Holmgren:
Also, the gentleman who has been talking here is Mark Rickert. Mark is on the program today to give us some more, I guess, expertise in this topic, because he's been doing a lot of work on the performance side of things. Mark is a principal software engineer here at Infinite Red. Mark and I have known each other for a long time. And he was even on the program before he was, I think it was React Native Radio 204, talking about dependencies back then. So, if you've listened to the program since we took it over, you've heard Mark before. Mark lives over in Asheville, North Carolina, but you're moving soon, right Mark?
Mark Rickert:
Yeah, that's right. I'm going back to Moab, Utah. That's where my heart is.
Jamon Holmgren:
Yeah, when you've talked about Moab, it has always been like, this is where you want to be. It's the type of place you like. And you have a house over there, right?
Mark Rickert:
Yeah. I bought a house there and lived in it for about a year and a half. And then my girlfriend got a job in North Carolina, so I'm in North Carolina now, but we're going back.
Jamon Holmgren:
Yeah, that's awesome. It'll be great to have you back there. And also with me is Jon Major Condon. Jon Major lives in Janesville, Wisconsin, senior software engineer here at Infinite Red, edits the React Native Newsletter, does not have a Mac book Pro M1 Max coming, unfortunately.
Jon Major Condon:
No, unfortunately not.
Jamon Holmgren:
Shame.
Jon Major Condon:
All my money's wrapped up in Shiba. Shiba Inu.
Jamon Holmgren:
Well, maybe if Shiba does well, you could afford one.
Mark Rickert:
I've been making a lot of money on Sheba.
Jon Major Condon:
There you go.
Jamon Holmgren:
I bought like, I won't say how much. I bought a bunch of Shiba, and then when it spiked up, I sold what I bought it for, the amount that I put into it, and I still have a bunch left, so it's like free money, right?
Jon Major Condon:
Nice.
Mark Rickert:
Free money.
Jamon Holmgren:
Mazen is not here for this up episode, but we are doing a part one of part two, so hopefully he'll be back for part two, and we're going to go ahead and talk with Mark again about performance, because we have so much to talk about here.
Mark Rickert:
Yeah, it's a big topic.
Jamon Holmgren:
It really is. It really is. So let's roll forward into the topic. This episode is sponsored by Infinite Red. We are premier React Native design and development agency located fully remote in the US and Canada. And soon to be Moab, Utah, I guess. If you're looking for React Native expertise for your next React Native project, hit us up. You can learn more on our website, infinite.red/reactnative. And don't forget to mention that you heard about us through the React Native Radio Podcast. And if you're a senior level React Native engineer located in the US or Canada, we are hiring. Go to career.infinite.red and submit the form.
Jamon Holmgren:
All right, let's get into the topic for today. The title is slightly provocative. It is, Performance Enhancing Drugs for your React Native App with Mark Rickert. And this is part one of part two, and we're going to be talking more in part two about some other, well, we just have so much to talk about. So, first off, I'd like to talk about React Native performance in general. This is a frequent question that pops up when people are introduced to React Native, it seems weird. I even had this pro or question when I first heard about it. I'm like, "JavaScript. Oh, you're talking about PhoneGap or Cordova," right? No, no, we're not. But you're you are running JavaScript on device engine, either JavaScriptCore or Hermes. And you're talking over a bridge, which means you're serializing stuff and sending it to Native and then that.
Mark Rickert:
Serializing with JSON, which is not fast.
Jamon Holmgren:
Exactly. And so it's like text, it's a big string that goes across and then it comes back as text as well it has to be serialized and deserialized four times just make one call. Right?
Mark Rickert:
Mm-hmm.
Jamon Holmgren:
And so that does seem like it'll be slow and it can be, but that's also not the full story. There's a lot more to performance than just that. There's no silver bullet for performance. You can't just flip a performance switch and then all of a sudden it's fast. Do either of you remember the turbo button on old 486 computers?
Mark Rickert:
Oh yeah. Oh yeah, that thing was permanently on, on my machines. Just press it and everything just gets faster.
Jon Major Condon:
No, but you are making me think about the shoes where you can, they're like basketball shoes and have like a basketball logo on it and you press it to pump up the pumps.
Mark Rickert:
The pumps.
Jamon Holmgren:
Yep, exactly. Yes. Yes.
Mark Rickert:
Nike pumps from back in the day.
Jon Major Condon:
Yes.
Jamon Holmgren:
Oh man. Those were such a big deal when they came out. I think I was in middle school or something. Mark and I are the same age. So we probably remember the exact same thing. I'm sure I had knockoffs. They weren't Nikes. But yeah. No, that's totally, the only time I would turn off the turbo was if I ran a game that was not, most of the games back then were not set up for different timings. They would be optimized for a certain computer. And if you had a faster computer, you had to turn off turbo to make the game playable or else it'd just go too fast and you couldn't, the clock speed was too fast. We don't have that, unfortunately.
Mark Rickert:
No.
Jamon Holmgren:
We can't just do that. Although I will say when the new architecture arrives, that may be like a turbo button, but we'll be talking more about that later. But yeah, let's ... I don't know. What do you guys think about just React Native performance in general?
Mark Rickert:
Yeah, I think in general React and by default then React Native is pretty performant. If you're writing a personal app or a small app, you probably don't need to do a lot of the things we're talking about today. But if you've got something that's hundreds of files and 150 different components, and just a very complex React application, you're likely going to be coming into performance issues. React is fast in general, but once you start adding computations in their, data transformations, calculating really large data sets and mapping over them, that's when JavaScript can block the main thread and cause your UI to just slow to an actual crawl.
Mark Rickert:
But I'd like to point out something that Donald Knuth, I think that's his last name, he's a Stanford computer scientist and he wrote, it's a series of books called The Art of Computer Programming. And he said, "Pre-optimization is the root of all evil." And so, I would say that you should do optimization when you find something that's taking too long. Don't just optimize things before you've even tested them. Because it could be that, it might be best practice to wrap a function in a useCallback Hook, but it might actually take more overhead to create that callback than to actually just re-render the function every single time. But we'll get more into that here soon.
Mark Rickert:
I think today we're going to talk about things that we can just keep in mind as you're developing to write the most performant code possible. And then later we're going to talk a little bit about performance tools that we can use to measure, which is always great when you're talking to a client, you're like, "I sped it up by 212%." I'm like, "How do you know?" And it's, "Because I profiled it. I ran the app in the exact same situation on two branches and it told me exactly how much speed I gained."
Jamon Holmgren:
Yeah. And of course all of these statistics can be misleading if they're in the wrong vector, because what is performance in terms of a mobile app? Is it more straight lane speed or is it perceived performance? So for example, if you have a backend app, let's say on a server or it can even be on a mobile app, you don't really care ... Let's say that you ... Okay, actually, let me go this direction. Let's say you're mining Shiba on your mobile app and it's going in the background, you don't really care if it stutters and stops and stuff like that as long as the overall output is more Shiba. And you can measure that by Shba per hour. Right?
Mark Rickert:
Right.
Jamon Holmgren:
And however, if you an app that works extremely fast, but every one second it pauses for a 10th of a second. Every second, you have a 10th of, or every 10 seconds or whatever you have a 10th of a second, people are going to get very frustrated, even though the extreme speed in the middle is, you're not looking for total out, but you want smoothness. You want things to be smooth and feel buttery.
Mark Rickert:
Right. Nobody likes a stuttery scroll view.
Jamon Holmgren:
Yeah, exactly. So is faster always better?
Mark Rickert:
Well, so faster I think is always better. But sometimes it just can't be faster. Like your server takes a long time to respond to a query or something like that. That's a server team's job to make that feel faster. But in that case, what you can do is try to make it feel faster with some techniques like making sure the user understands that there's something going on instead of just waiting for the response to come back, let the user know with a spinner or a pull to refresh or some indication that something is actually happening. Because if a user doesn't see anything happening and doesn't see the data they want on your screen, they're going to think something is slow.
Jamon Holmgren:
Yeah, and there's even situations where showing a spinner can make it feel slower. So you have to really think about it. Like if you always show a spinner immediately, people are going to be like, "Oh, there was a delay," where they may not have noticed it otherwise. So it's very situational. Isn't it?
Mark Rickert:
Exactly. Exactly, yeah. And there's a couple things to take into consideration, especially when we're talking about React Native app performance. We've got boot time. How long does it take for the apps, native loading screen to disappear and for React Native to start doing its job?
Jamon Holmgren:
Yeah.
Mark Rickert:
Right. And the way you increase that is optimizing your native code. And that's not really what we're here to you talk to about. So I think we're going to be talking mostly about things after boot time. What we can do to increase our, what's called time to interaction, which is how long it takes for your navigation stack and home component to render? Like if you've got a tab bar and users can return back to a different tab, it depends on the home component on that tab as to how fast it loads when it re-renders. So there's quite a few things to take into account when we're talking about React Native performance.
Jamon Holmgren:
Going back to boot time for just a second, one of the things that will help a lot with that will be turbo modules, because instead of loading all of the native modules immediately before it kicks off the JavaScript side, it will actually load them only when needed. So it'll be lazy loading for native modules. Turbo modules is going to help a lot with the boot time, if you have a lot of native modules that are slowing things down. So that is a note to make.
Mark Rickert:
Yeah, definitely. Another thing that is going to improve your performance is getting rid of your console log statements. If you've ever had a console log in a big loop or in the render function of your component, and it just runs all the time, just removing some console log statements is going to increase the performance of the app, because those are huge performance killers. We all know that there's a bubble transform plugin for React Native that when you actually go to release in production mode, it'll strip out all the console.log statements and all the dev statements and stuff like that. So, definitely running in production mode, you're going to get a lot better feel for how fast and smooth your application is. Especially if you have a lot of console logs going back and forth over that little wire, connecting your device to your terminal.
Jon Major Condon:
Yeah. And definitely check for other Babel plugins that do clean up things such as test-ids, and then also optimizing images and so forth. There's a lot out there.
Mark Rickert:
Yeah. Well, I think we're going to get into images here in a little bit.
Jon Major Condon:
That is true. Yeah.
Jamon Holmgren:
Yeah, totally. So, the first place I usually go when I'm looking for topics like this would be the documentation, and they do have a performance section of the React Native documentation.
Mark Rickert:
Yeah, the React Native docs are really great for giving you an idea of how something should be done. And when you're working in a language like this, you spend a lot of time working with libraries and components you didn't write, you're using other people's modules. You should read the manual for everything. RTFM everything. And that's what's going to get you into a mindset of, how do I make this thing the best it can be? Instead of just starting typing and relying on IntelliSense to give you the properties you think you might need. I spend a lot of time in the documentation for libraries and for React just researching and stuff like that.
Mark Rickert:
But there's also TypeScript definition files that, I use VS code and I can just hit command and click on the thing and it'll open the TypeScript definition file. And I can scroll through and see a list of every single property that this can have. And generally good library maintainers will have inline comments on those TypeScript files that give you the documentation for it. So TypeScript is definitely a really great way to make sure that you know what these components can do that you didn't write.
Jamon Holmgren:
Yeah, totally. And if you go through the React Native performance docs themselves, the page that I was referring to, we will put this in the show notes by the way, one of the things they mentioned is, 60 fps frames and there is the famous 16.67 milliseconds number. And I know you guys know this already, but I'm going to explain for our listeners what the 16.67 milliseconds is all about.
Jamon Holmgren:
If you take 60 frames per second and you divide that by the number of milliseconds, you're going to get 16.67 milliseconds per frame. So you need to do all of your work, everything that has to happen, you have a budget of 16.67 milliseconds, if you want to get done before the next frame, because if you don't, you're going to drop a frame and it's going to stutter. You're going to feel that. It's like, "Oh, that, I missed something." There's a little jerk in the scrolling as it happens or the animation or whatever.
Mark Rickert:
Yeah, and React Native actually has a tool to help you with this. And I'm going to put you on the spot here at John Major. Have you ever opened the React Native performance monitor?
Jon Major Condon:
You know what, on native, no, I have not.
Mark Rickert:
Okay.
Jon Major Condon:
And I honestly think because React Native is ready to just perform it for me already. When it comes to React JS though, working on bigger projects on the web, I have many times, and it is a life saver.
Mark Rickert:
It definitely is. And it's actually really easy to get to in your React Native app, once you've built it and you're running it on the simulator or on your device, it works great on Android and iOS, just shake it, bring up the menu, and there's enable or disable performance monitor option in there. And that'll put a little floating window on top of your React Native application. That'll show you your FPS and memory usage and stuff like that. And it actually shows a little bit more info on Android, like number of dropped frames and stuff like that, that it doesn't show on iOS. But you can click and drag that around your window. It's annoying though that it's on top of everything. And it always starts in the upper left hand corner, so you always got to drag it around. But that's how you get it open and I work with that on almost all the time. But that's because I'm mostly doing performance enhancements right now.
Jamon Holmgren:
Yeah, and it's showing two frames per second numbers. Can you explain what those are all about, Mark?
Mark Rickert:
Yeah. So the UI frame rate is, how fast the application's native UI is updating. And then the JavaScript frame rate is, how fast the JavaScript main thread loop is running. And so your actual JavaScript FPS monitor might freeze if you lock the main thread for a while. If you're doing some heavy computations or something like that, it could say 60 frames a second, but your app just doesn't respond. And then two seconds later, the graph will update itself and you'll see that it was zero FPS.
Jamon Holmgren:
Yeah, and those two numbers are connected, but also decoupled to some extent. So you could, for example, be in a situation where you have a native component, like a scroll view, or it has a native part to it, and the scroll goes up and down just fine. It's buttery-smooth, but your JavaScript side of things is doing something else. And so it's not catching up. And we've run into this before where you're scrolling and you're wanting these on scroll events to happen, but they're only happening every second and you want it to happen every, a lot more often than that, right?
Mark Rickert:
Mm-hmm.
Jamon Holmgren:
And so you're not catching the scroll very often. And that really into effect when you're trying to make something, animate during a scroll or something like that. So you have to be thinking about both sides of the equation, the JavaScript thread, as well as the main UI thread.
Mark Rickert:
Yeah. And if you're doing some heavy computation on that on scroll event there, you should also add a scroll throttle to the scroll view. There's a property where it will limit how often that function callback fires.
Jamon Holmgren:
If you don't need to do that every single scroll event, you can just, if it's something just not as necessary. I think this is a common theme. So like way back in the QBasic days, when I used to make little games and stuff, I even understood this. And I would do things like, let's say you're calculating a path to, a little character needs to walk through a path, you don't need to calculate that every single frame. You don't need to go back and run that path finding algorithm every single frame. You can just save the result of the path finding algorithm, and then the character can just walk along. And then if it encounters something new that it didn't see before, like someone walks in its way, then it can be like, "Hey, I need a new path." And it just calls it again. Or you can do it every 10 steps or every 60 steps or something like that, so you're not just constantly recalculating and doing stuff like that.
Jon Major Condon:
Though this seems like a request animation frame, how does this differ from that?
Jamon Holmgren:
So request animation frame is a great tool that we have. And what that allows us to do is say, after the next frame hits, I want to run this. So it's like, I know that I'm exceeding my budget right now, I'm getting close to that where I'm going to start dropping frames, let's put that into the next one, request animation frame. And so that callback that you get there is really helpful for that. We're going to be talking more about this as we get into part two specifically, about request animation frame, but that's definitely an awesome tool in our toolbox.
Mark Rickert:
We got a little teaser there for part two.
Jamon Holmgren:
Exactly. You got to tune in next week. So I actually wrote a tweet a while back, and I said, one of my developers just messaged me, and that was mark by the way, "The performance improvement in the React Native app after this fix was like, knock your socks off better." And then I wrote, "The ironic thing, it was some native code Objective-C that was causing memory leaks and performance problems."
Mark Rickert:
Yeah. We're dealing with a much more complex ecosystem here than just native developers have to deal with. We have to know JavaScript. We have to know React. We have to know TypeScript usually, Objective-C or Swift and Java and ... And so there's a whole bunch of things that we need to be able to know and understand how they work together in this ecosystem. I think a React Native developer should at least know a little bit of everything there, Java, Objective-C, definitely a lot of JavaScript though. But yeah, having such a complex ecosystem means that bugs might not always come from where are you expect them to be.
Jamon Holmgren:
Yeah, so explain a little bit more about that particular problem. That was like an ad banner that was getting allocated, and basically, it was a memory leak. They kept allocating more and more, right?
Mark Rickert:
Yeah, it was a memory leak in a custom component. The issue you have sometimes with React Native projects is you get all kinds of developers coming into it. They're like you got web developers coming in, you got Swift developers coming in, you got Android developers coming in. And they might not know all the best practices, and they might not know about memory management and how it's a much bigger deal in Objective-C, which this code was written in. And yeah, essentially it was just a memory leak where we were de-referencing a value and not removing its delegate, so we were just holding onto that reference.
Jamon Holmgren:
Yeah, there's a reference to this pretty heavy honestly native component. And so it was just piling know everything. I don't know if it was like, every time you'd scroll or something like that, it would just pile up more and more. And everybody's like, "Why is this thing so slow?" Mark comes in there and you figured this out later.
Mark Rickert:
Oh man, it took me a long time to find this one.
Jamon Holmgren:
Yeah.
Mark Rickert:
I would just watch the memory usage of the app grow and grow and grow and grow until it finally crashed after 20, 30 minutes of extreme usage. And yeah, I knew it had to be some kind of memory leak in the native side of it. So I literally just went looking and I just started reading code. And this one little thing popped out to me in my mind after about two weeks on and off of this task, trying to figure it out, wondering where it was. And so, sometimes these can be very frustrating bugs, but once I fixed it, the performance in the app was, again, knock your socks off better.
Jon Major Condon:
So for our listeners that don't have native experience, is there something in the short term that could be done on the JS side to help with some or most performance issues?
Mark Rickert:
Well, yeah. Absolutely. If you find that something's jittery, or taking too long to load, or is killing the JavaScript frame rate or something like that, you should start using the profiling tools that are available to you. There's things like Flipper and the React Devtools library and Reactotron that can help you profiling your application. And generally what I'll do is I'll just fire it up and reload the application and profile as soon as I'm allowed to in the boot sequence. And then once the app boot's up, stop the profiling and then just look at the React profiler, and you'll be able to see which frames were taking longer to render, and you can dig into which components and which functions and stuff like that really just took longer than you wanted them to take. And so you can do a targeted strike on your code base for the things that are taking the longest and are slowing down your boot sequence or slowing down your renders.
Mark Rickert:
Another thing you can do is move data computation into your data store. We use MobX-State-Tree at Infinite Red, and they have this idea of computed values using getters and so, anything that can be stored in the model should be, and you should just reference that computed value.
Jon Major Condon:
So, Mark, I want to put you on the spot like you did with me. So in Slack, I remember you had messaged a pretty cool performance improvement, and that was something to deal with section list. And I want to, I guess, have you explained what those two lines were that fixed the problem for you.
Mark Rickert:
You'll have to remind me. Did it have to do with initial number of items to render?
Jon Major Condon:
Yeah, max to render per batch.
Mark Rickert:
Because max to render per batch, exactly. So React Native actually has a specific page in their docs for performance improvements on virtualized lists, so section list and flat list, and there are a lot of default values that a lot of people don't use or know about. And it can really help, especially if you are rendering very complex things. React Native tries to make section list as fast as possible, but they have to rely on defaults. And so they just guess at how many things that they should render at one time. And they render things in batches. And so you can say, "Okay, the next to batch of renders, I only wanted to render three items instead of 10 items." And so that will speed up that next render cycle at the expense of having to render more often.
Mark Rickert:
So, that's something where you can do with virtualized lists in React Native that will really help increase your performance. Do you know that when you're scrolling in a virtualized list, I think it keeps like 20 pages, or maybe 21 pages. I think it's 10 above the window, 10 below the window, and then the page that's in the window with all the items. So yeah, digging into how the virtualized list component can provide you with these sorts of options to customize how your list renders is going to really be a big benefit for you in terms of performance.
Jamon Holmgren:
Mark, you have a note here about avoiding anonymous functions. I don't think this is necessarily a performance enhancement by itself, but it helps you track down performance problems, right?
Mark Rickert:
Yeah, exactly. So if you fire up the performance tools in Flipper, let's say, it's going to tell you how long each view and component and everything rendered in a big, I think they call it a flame graph.
Jon Major Condon:
Yeah, this is similar to what's in React JS and the devtools where you that flame graph ... Oh, I'm sorry, this is actually Chrome devtools. And that flame graph, I remember seeing anonymous functions left and right on projects that I've entered in on and trying to track down what those functions are.
Mark Rickert:
Right, because obviously the anonymous functions don't tell you where they are, they just tell you what their parent is which is usually the main component that's rendering. And so if you pull all those anonymous functions out of your JSX and you define them in your component body, that way you have something named that you can go back to. Just anonymous functions in general, I try to stay away from them because when I am profiling the app, it makes it a lot easier to figure out where they are, what's taking so long and fixing it.
Jamon Holmgren:
So you can't really necessarily use the fat arrow functions instead you'll be using function and then the name and the function keyword. And you can also make anonymous functions using the function keyword by just not giving it a name function, and then parentheses, but avoid those as well. Sometimes it looks a little weird because sometimes you'll be doing like const my function equals function my function. You're saying it twice, but hey, it's better to do that and be able to track things down than not.
Jon Major Condon:
So what are some tools to use for analyzing performance in React Native apps?
Mark Rickert:
Yeah, I've already talked about a couple of them already. The one I'm using right now is Flipper, Facebook's React Native devtools sort of thing that they provide and ship with React Native. It's kind of like a Swiss army knife. It's got all sorts of things; logging, debugging, and there's plugins that you can get for MobX-State-tree and Redux so that you can modify state on the flyer using Flipper. But it includes probably one of the more powerful tools, which is the React devtools in there. It's really great because it's got a button where you can just click and reload the app from Flipper so you don't have to shake your device or hit command control Z or command R, you just click the button in Flipper and it automatically reloads the Metro bundle. So that's pretty cool.
Mark Rickert:
But the React devtools, you can run it by itself in its own app, but it's integrated into Flipper. And so I've been using it there. It has two really awesome tools that I use all the time. The first one is the component tree view, which helps you see the entire hierarchy of your application. And if you've never looked at one of these things, they're pretty gnarly. You've got all of these views nested within views, nested in within views and some of them you don't even know where they're coming from because they're automatically injected by things like React navigation, or some other native modules you're using, or you'll see the context a lot if you're using React context. It just wraps everything automatically in that context so that you can use it. So it helps you figure out why is this object here when it should be over here? And so it just gives you a big tree that you can go through and see what's rendering on the screen and why it's there where it is in relation to all your other components.
Mark Rickert:
And then the flame graph profiler is a really, really great tool where essentially you can start your app and profile it, do something in the app and then stop profiling. And then it will give you basically a list of how long everything took to render itself and its children. And you can drill in to each one and see how many milliseconds it took. And then from there you can figure out, okay, if this component took a long time to render, where is that coming from and how do I make it faster? So the Chrome dev, all right, the Chrome devtools, the React devtools, which is also available in Chrome you can run it there as well. It helps a lot for giving you a hit list for each render cycle and what takes the longest so that you can focus your efforts on where you're going to make the most impact in the app. Because if you try and go around and optimize your app by throwing useMemo and useCallbacks around everything, it's not going to make it faster. I can tell you that.
Jamon Holmgren:
We're going tom talk a lot more about that in part two, because we have a whole section on hooks and how to optimize those.
Mark Rickert:
Yeah, absolutely.
Jamon Holmgren:
It's going to be great.
Mark Rickert:
I also found a really cool project called Why Did You Render? It's by a guy on GitHub Welldone Software. We'll put a link in the notes. But you basically integrate it into your app in dev mode and then turn it on, and it will monitor your entire app and tell you every time something re-renders and why. And a lot of times it'll tell yo this thing re-rendered, but it didn't need to because the shallow prop comparison was false or something like that. So Why Did You Render is another tool on the tool belt of the React Native developer.
Mark Rickert:
I have it integrated into some of my projects and it's always off by default because it just generates a ton of console logs. This is another time for using something in a targeted manner, turn it on when you need it and make sure it's off so that the rest of your team doesn't hate you. But yeah, console log is also a really great way to figure out what's going on. I use console log all the time, but I never really leave it in my code. But just to figure out what's happening in what order, sometimes console log is your best friend to just figure that out really quickly so you can fix a bug.
Jamon Holmgren:
So what you're saying is that console log can both be a fix and a cause for your performance problems?
Mark Rickert:
Yes, it's like alcohol. It's the problem and solution to all.
Jon Major Condon:
Use it wisely. Don't be addicted.
Jamon Holmgren:
I love it. So Mazen who couldn't be here for part one here, he had a note about post-processing your project. So there are some things that you can do to improve apps performance after you've actually programmed your app. One of them is images. How can you optimize the images that are brought into your project?
Mark Rickert:
Yeah, definitely. You always want to use vector images if at all possible. If you're working with a design team and they've got a design for you, and they hand you a bunch of bit maps, you're probably going to have a bad time. Bit maps in general take longer to read from disc and render. And if you get an image that's too big from a designer and nobody notices, it's like four times the size of the container that it wants to be in. So you're like, "Oh, I'll just strap resize mode constrain on this image property." Well, that actually is handled on the native side. So it just gives iOS and Android that image container and says, "Make the resize mode constrain." And it relies on the native part to actually change that image and scale it down.
Mark Rickert:
And actually on iOS, that's very, very slow, to be frank. And so whenever you're putting an image in, you should always make sure that your images are sized appropriately for the container that you're putting in. Use thumbnails whenever possible, or maybe even some sort of server side image size changer. I don't know. I've used those in the past.
Jon Major Condon:
Like, Cloudinary.
Mark Rickert:
Yeah, exactly. I've used those in the past to say, "I want an image and I want it this size," and it'll return back to me a bit map that's the proper size for my container. You should ... Resizing images down is never really a good idea in React Native because it can, especially in list views and stuff where you generally have a large set of images along the left hand side or something like that. If you've got every single one of those images is 1000 pixels by 1000 pixels and you only need it to display in a 50 pixel box, that image should be 50 pixels or 50 at 3X so that you can make sure that you're not doing any completely unnecessary re-rendering on the native side for those image sizes.
Jamon Holmgren:
Yeah. And I found a really cool tool called Expo-optimize, which allows optimizing not just Expo projects, but really any project that has embedded images. And it won't resize things for you, you still need to do that yourself if you want to do that, but it runs through and compresses your images to a degree that you're not going to necessarily notice it yourself, but it will save you a lot of disk space.
Mark Rickert:
Yeah, definitely. Don't ever trust an image that was it's given to you by a designer. Always make sure that it's minified, optimized for native. Because if you're including these images in your app bundle, those large images are also going to increase your download size as well. And so part of optimizing is also making sure that your bundle size is small enough that can be downloaded over cellular. Nobody wants to download a 200 megabyte app, when they're out and about.
Jamon Holmgren:
So I purposely left this out of our tools discussion because I wanted it to have its own section, but Reactotron, which is the desktop app that we use at, that we built at Infinite Red, actually has some performance benchmarking tools, which is pretty cool. And basically, if you have a function that you think is slow, you can benchmark it and you can use Reactotron.benchmark, or if you've hooked it up to console.tron, you can say console.tron.benchmark, and then you just do steps through your code. And then the last one is .stop. And then when you call that, it will actually show you this really cool graph in your Reactotron timeline that is very targeted to the thing you're benchmarking. And so you can actually see it looks like a Gantt chart or something like that, where you have your different things that you're logging out and it shows you how long they took and sort of a proportion in the bar chart that you're putting out there, execution time. So it's a really cool way to dig into one particular slow function.
Mark Rickert:
Yeah, I can see how that would be a little bit better than Why Did You Render, which basically just spits out everything. You can configure it to just do one thing. But I actually haven't used that in Reactotron, that looks really neat. I'm going to have to give that a try.
Jamon Holmgren:
Yeah, I'll link to it in the show notes as well. It's something that, just like everything in Reactotron, we don't even promote it well enough internally, what it can do, much less externally, so that's something I just wanted to mention.
Mark Rickert:
Nice.
Jamon Holmgren:
All right. So I think we're out of time for part one. We have part two coming up. In the meantime, where can people find you online Jon Major?
Jon Major Condon:
@jonmajorc.
Jamon Holmgren:
Mark?
Mark Rickert:
I'm on Instagram and GitHub as markrickert.
Jamon Holmgren:
And you can find me @jamonholmgren. You can also find React Native Radio @reactbativerdio. Thanks of course, to Mark for joining us today for part one. He'll also be here for part two. And as always thanks to our producer and editor, Todd Werth, our assistant editor and episode release coordinator, Jed Bartowski, our social media coordinator, Missy Warren, and our designer, Justin Huskey. Thanks to our sponsor Infinite Red. Check us out at infinite.red/reactnative. Special thanks to all of you for listening today. Make sure to subscribe and come back for part two. Reminder, we are hiring at careers.infinite.red, and we will see you next time.