React Performance

Pushing State Down Exercise

Steve Kinney

Steve Kinney

React Performance

Check out a free preview of the full React Performance course

The "Pushing State Down Exercise" Lesson is part of the full, React Performance course featured in this preview video. Here's what you'd learn in this lesson:

Students are instructed to refactor the component hierarchy, so the input field becomes responsive again. Steve then walks through a possible solution to the exercise and student questions regarding whether memo could be used on the expensive component or on a component that is displaying a 3D graphics canvas.


Transcript from the "Pushing State Down Exercise" Lesson

>> So guess what's next? What could be next? There's another branch on everyone's favourite game called pushing state down, the exercise. I could tell you where the bug is, by fill the branch name, and everything I just said in the last five minutes spoils it. My face is different last time, but I've moved the problem.

What's gonna happen? It is once again slow, who 400 milliseconds this time. And as you can see, it's pretty easy even in the reactors to see where the culprit is, right? Yeah, application is long, but it's got an equally long child in it as well, right? Which to be clear is named ExpensiveComponent.

I don't think there was a mystery of where it was this time. It was hiding in plain sight. And again, if we kind of look even though I said I don't think we really care about the componentry at this point, it kind of gives a sense which is application needed to re-render.

The fact with the packing list that input didn't, I guess it's kind of the opposite problem, right? But application, the rest of these things cared. It was really just this expensive one over here that didn't, right? And so in this case like before, we took that input field and packing list and we pushed it down by itself, right?

In this case, we're kind of going to do the same idea, but in the opposite approach which is ExpensiveComponent doesn't even take any props. So it certainly doesn't care about that input field, right? So if we push everything else down a level, they won't talk to application. They'll talk amongst themselves, and not bother application, which will then not bother expensive component, right?

And we get the same performance gain as we got before by just same philosophy, opposite. Before, we just took the thing and pushed it down. Now, we're taking everything else and isolating to make that ExpensiveComponent now like the crazy uncle or something like that. So we'll go ahead.

And this is a very heavy refactor, which is, I selected it all except for ExpensiveComponent. I cut, sure we have a CSS issue at some point. We'll make a new component, I'm gonna call it game. And we will then go ahead and. Call it game. And I gotta put some wrapper around it.

We have lots of imports yelling at me, but those are easily, let's find out. We were joking about auto import and half the time, it's amazing. Will it serve me, these are pretty safe ones. Just let them all kind of auto-import their way, and the rest of them are variables.

Cool, cool. I do need to grab some, let's say that we were changing. Pretty much, all of it since nothing else, ExpensiveComponent that really doesn't care. Grab that. Probably back for some of the CSS classes in a second. Awesome. Cool, cool. All right, so now, it should be mostly there other than the fact that I have a whole bunch of useless stuff in here.

And we got to actually put the game component in there. So, now all of those state changes happen down a level on the tree right inside game. ExpensiveComponent still is expensive, right? We didn't make ExpensiveComponent any less expensive. What we did was basically just say, no one's going to bother ExpensiveComponent.

Just because it happens lower in the tree, React doesn't even think to touch it. And if you don't call it, it doesn't really matter how slow it is except for the very first time. And so we just kind of use our state hierarchy or component hierarchy to make your app more performing without doing anything else, but then thinking in the way that React would prefer us to.

So, we can kind of go back over. And yeah, could I use some more spacing between those things? And a little bit of flexbox in the gap, I absolutely could. But you're gonna say now, It's pretty snappy again. Application did not render, ExpensiveComponent did not render, right? They are left alone, and nothing else really even counted.

Which is pretty cool, right? They didn't. The nice part is they all go gray when they don't render at all, which is nice. Cool, and that is one way to handle this, just by thinking you take the expensive parts and try to separate them from other parts of the tree.

Whether that's taking everything else and pushing it down, or pushing it down, whatever the state is depending on the nature of the application. It's not just a one size fits all, if it's like, hey, it's the only thing that needs it, you push it down, or the expensive thing is the only thing it doesn't need it, you push everyone else down, right?

You just separate the problem from the rest of the group. So one bad apple kind of thing. One question, can use a memo on ExpensiveComponent? You could, yeah, right? However, this goes back to the thing of, so the question was could I grab an ExpensiveComponent and we'll use React memo a little bit?

Could you say memo which is a higher order component, which we'll talk about. And it says like it, higher-order component is a component that wraps another component. A lot of times it doesn't have maybe any UI of itself. It's just receiving the props beforehand and then passing them through.

And so memo is a higher-order component built into React. And what it does is it sends me forward that should kind of update and some of those other things. It looks at the prompts before we pass it to the problematic component, or just a component we wanna stop, right?

And it says, are these prompts any different, right? And if they're not, then, I'm not gonna bother the component that I'm wrapping. It's sleeping, it's not causing problems, we'll stop this here either cuz it's expensive or it has one dillion children that's gonna start a whole cascading tree that could have one expensive component in it, right?

Well, let's leave this one alone. And particularly for components that don't take any props, right? Guess what? Nothing and nothing are always the same, right? And so, in that case, in the shallow comparison, which is just the first level of everything. It's cool, nothing, awesome. Yeah, and so that will stop it from rendering, right?

However, also it's not going to render again, because we did the component hierarchy. So in this case, could we do it? Probably, would I do it? Probably, it's actually slight cost because we put functionality that checks the components not running, but it's also not rendering. It's not going to gain us anything.

But I would probably still do it, because it's always that thing like, what's happening in your code today? Is not necessarily what's happening your code nine months from now, right? And so sometimes you think about future, you look forward and do things that maybe if we're being pedantic, you're like well it's not doing anything but it's something else is gonna appear in application eventually.

Let's do the thing, right? But yeah, memo throw it. I don't have a problem with doing it. What I would love to caution us against is just having that be the first thing we reach for because pushing the site down is free. Rubbing everything in memo as we saw before, it can slowly become the problem in and of itself, right?

We don't want the medicine to be worse than the disease. Awesome! So that was an awesome segue, because it kind of covers a little bit of the next thing we're gonna talk about. You know, doing less stuff takes less time. So we pushed the state down. We did in this game.

All right, cool. Yes, Mark,
>> Would it be okay to use Memo in cases where a component is doing weird things like displaying a 3D graphics canvas, and you don't want a component to ever re-render because you only want one canvas?
>> Yeah, it's tricky, right? Cuz that will only work if it doesn't ever get new props, right?

We could argue, is that the right way, is a use effect with no dependencies. If it's just drawing onto a canvas, you probably have a ref to that canvas. And like probably see if a ref has a current, and if ref has current not doing the thing, that's the right answer.

[LAUGH] I don't think about the question. Given the options of wrapping it in a memo, so never re-renders. A use effect that only does it the first time, in this particular use case. Those other two aren't bad. But in the case where I needed to get that canvas element anyway, once it has a canvas element and is drawn to the canvas, ref.current will not be false here, will not be undefined, right or no?

Which point you should then be able to never do it again? ref.current is used for a lot of hacks in this area. Obviously, it was meant to reference a DOM node, but in the same way state changes on every render. If you needed whatever the new state was, and the previous state was, ref has what I had last time.

So a lot of times you'll see move where it's cool. Store the current state and a ref. And then before we restore it in there, we can see the previous state and the current state. You can do stuff like that with it. Is it intended? Is it done frequently across probably half the libraries that you use?

Maybe [LAUGH]. So yeah, it's there's a thing. But to show you the power of this, a large application you can see that, this technique of not even using memo or anything like that does pay off and is definitely worth investigating and evaluating. We'll see it's got a sibling kind of methodology that we'll look at too.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now