React Performance

useReducer & dispatch

Steve Kinney

Steve Kinney

React Performance

Check out a free preview of the full React Performance course

The "useReducer & dispatch" Lesson is part of the full, React Performance course featured in this preview video. Here's what you'd learn in this lesson:

Steve demonstrates using useReducer and dispatch to replace useCallback hooks and dispatch the items for the item lists. A bonus round of replacing useReducer with useState is also covered in this segment.


Transcript from the "useReducer & dispatch" Lesson

>> This is where another thought exercise that literally considered and thought about and then opted not to do, because I think that it's probably a bad idea. So these packed and unpacked items are generated on the fly, right? They're generated from taking the overall list and you filter them, right?

If there were a lot of changes that didn't involve them moving back and forth between the two lists, right, one thing I could do is I could wire up my state management in a way that had two lists, right? And then I would manually have to shuffle them back and forth, right?

And you'd want some unit tests with that, even in that simple example, you want to unit test. And then I could then memorize or have things based on each of those. But then I'd have to have each of those add, update, and remove functions. And I would have to do the act of shuffling them back and forth.

And sometimes, one, I don't actually know of all that extra work and having double of everything and following stuff back and forth is worth it. And two, the complexity cost is also probably not worth it for almost no performance gain, right? But again, you have options, and to always reach for one thing blindly, is probably at least slightly problematic, right?

But you can also again, you can get a look at that flame chart and be like, are the things that I'm expecting to render or rerender actually doing that in this case, right. One thing I'm curious about too, just to look real quick, is I would expect an individual item isn't.

They're getting a new update and remove. So, could we make this better? I think we could. Let's do it. All right, as I said before, we're getting the update if we pass either set items or dispatch all the way through, those stay static, they don't get regenerated based on items.

All right, but then the problem with passing set items through, is what does it need? I mean, theoretically, it needs a list of items, unless we use the function callback. So let's do it. I could do it either way. We'll get the same effect. Let's do it with a reducer, but also using the function callback would work.

So check it out. It's almost like I had pre baked reducer, it does all the things that you think, it adds stuff, it removes stuff, so on and so forth. So in this case, what we'll do is we'll change this to, Use reducer. We'll give it that get initial items, Which is literally just returning a JSON array.

I don't even know why it's a function. And so this becomes called Items. Yep and we'll call it dispatch now. Now, dispatch just fires actions. It doesn't actually need to know what the current items are. It's just saying, hey, this item, we're toggling it, right? And the reducer gets everything and figures it all out, right?

As we know that setItems, if we still had it, you could do this as well. It's really, setState being a, or useState being an abstraction over useReducer it's not even to call them different things, they do do either way. It's different flavors of the same way. This would give you a reference to the current items that you would not have to pass through.

But then, again, I know you could do it, you could do it for some of these and make it all kind of happen, right? I'll leave that one the use reducer, because it's just different and I can make a point and you can do some of the items here.

So now we're gonna pass dispatch all the way through, which means really, I get to do one of my favorite things in the world. Can't delete the two lists. That one, and we can see that instead of passing all these things through, we literally just say. Awesome. Do it here as well.

I'll pass it to the others and probably only to do the two lists in front of us because it's just variations on a theme. So pass it item list, in item list, we will go ahead really didn't need that at another time. I wouldn't know the pain. We'll swap out the fact that we're given an update and remove and we just give it that regular old dispatch.

And notice I got rid of all those use callbacks that I wrote, they're gone, right? So by definition, this is going to be slightly better for us. And then the individual item, setting an update and remove I literally have on my clipboard. Cool, and then we'll go down one, one deeper.

Get rid of it again. Now in so far that I'm very lazy, the action creators have the same names. So I just need to import them So now the only thing coming through is dispatch. So that now wraps each of these, these will dispatch those objects. It doesn't need to know anything about the items, it's just telling the reducer hey, they said they wanted to remove the one with this ID.

Reducer is like, yo I got all the items, I'll figure it out. I think that I'm accidentally getting myself the reputation as a reducer fanboy, and I'm here for it. I don't have a problem. So let's check it out. You see that I don't have the individual items flashing anymore, and we'll verify that in the tools.

Right, sure. The first time it was rendered, probably once it changed. But you can see at this point it is not rerendering them, because they're getting the same dispatch. They are the same item in memory. Because you just changed something, the array is different. All of the objects in that array are the same ones unless you change them, right?

That's part of the promise of the immutability, it's the same object. So that was fine. Just because it was a new array full of objects, doesn't mean the objects were different. It's just a new box that they're in. And so we got rid of all the use callbacks that we just installed.

All we have is that react memo that we had in the very beginning. And yet we actually stopped an entirely additional, removing a use callback and taking a different approach, was more performant than using the useCallback, right? And that's kind of a running theme that we have today, which is sure, these tools are great.

But are they the best one, right? Sometimes they are the right one. Sometimes the trade offs are worth it, but just being like, you have to do this blindly every time, is maybe not always the best way forward. And we could do it with that items too. Right, Let's go for a quick bonus round.

So we had use state. That should give us what they get initial items that we put in a function because we learned something earlier. This is now set items. So in this case we'll now change this. The joke I made the other day, which is anywhere near tax developers because multi cursor.

And if you hit some of the ones I didn't fully wire up the dispatch they will break, so just don't hit them. Cool, you can pass all these through, now what we should be able to do as I go off roading, is swap it in here And these are gonna be slightly different now, I have those helper functions that we're using.

So this is what update items. This now gets, we'll give the function version where it's like all the items And that takes all the items. The reason I wrote these in TypeScript was that I could have help the current What's the other one? And I need the updates.

In this case, it is just packed and we're switching it. I'm only going to wire up this one just to make the point. Grab the event. Cool, actually we want just the opposite of whatever it was, just like we had before, right? For name we might need that all but we're just toggling us.

All right, so I have to this, we'll see and then I will take Mark's question. We broke it. Mark, what's your question?
>> Why does use. Reducer work better than useMemo and useCallback
>> Because it doesn't need to be used in useCallback at all, useCallback is a way to say, hey don't give me a new reference to this function

>> For these reasons. But if any of those reasons change, I need a new version of this function. Dispatch is always the same one, right? It doesn't ever change. It is from the very beginning of when you call that hook. If it gives you a dispatch, which will always be the same dispatch.

So it eliminates your need for useCallback completely, the only reason we needed useCallback because we were whipping up functions on the fly. If we're willing to pass down, either the set items or dispatch, basically they're effectively useCallback way before we ever got it, right. Here we're taking something that was already useCallbacks and wrapping it in another one.

>> All right. So it's not necessarily that they're better morally. They're just, doing things less is better than doing it more, yep.
>> Is there any performance advantage in using Redux over how you're using the reducers here?
>> Yeah. So, like everything, it depends, right? As I called it name for some reason?

We're doing packed on this one. So having my talking type, but so yeah, the question was, is there advantages to using Redux? So, there are advantages to is using Redux, right, use reducer is great. Awesome. You get a reducer. You get dispatch, cool, cool, cool. Things you don't get, middleware.

Things you don't get, Redux dev tools. A few other things you don't get is like all of the TypeScript support that's built in React to Redux. Imagine once we get this thing now rendering, that we're gonna pull it, just hypothetically that the next section was like, let's wrap this thing in the context API, right?

You will see that all the performance gains that we got immediately go away, right? We'll get them back by doing a bunch of work, right? If you were to then do the same example as an exercise to the reader like later and just grab reactivated does the simplest version.

You would see that you would have all the performance gains out of the box for free, right for two reasons, right? Some of them are newer reasons and an older reasons, right? For two reasons. One is that those performance gains have been done for you. Two is that modern React Redux, at least the current version, I don't know when this changed, I verified by looking at the source code like a week or two ago.

It doesn't use the context API, it uses that sync external store. I literally almost typed it a second ago. And it uses that, which is like, because by definition in React, that user reducer is a hook, which means it has to be defined in that function, which means the store at least, or that state.

Is new every time, right? You think about when you set up Redux, you're saying out even before you did your first component, before you created that route, you're like create store, right? So that is a static object that is just floating there, the same one in memory. It has a get state method and a subscribe which just tells you to call get state, where it'll give you the newest and most immutable version.

If that state hasn't changed, you automatically get the same object as you got last time. It's only if it changed that you get a new object. The store itself isn't gonna change cuz it's in the initial scope that you defined it in. So, is it simply, it's better in so far that use reducer doesn't seek to be a complete replacement.

It seems to be in that there are certain distances that you will definitely walk to and you'll be like, I'm not taking a car there, right? And then after a certain point, I'm either gonna get the bike, or the car, or something like that. I think use reducers is like, I need a little bit of a reducer, I need something where I've got a form, multiple things changing and I'm deriving the state at the end.

Pulling in Redux this one feels like a bit much, right, or what have you, state management library of choice. I like Redux, cool, cool, cool. You might like mob x or what have you, cool. But there's usually, sometimes it's like, is it really time for this yet? Yeah?

>> [INAUDIBLE] Let's say just use Redux for the global part and use it to do search for the forms or individual pieces, where it doesn't feel like it's supposed to and global.
>> Exactly, that is that was the point I was gonna make, so I'm gonna repeat it like it was mine.

>> [CROSSTALK] so maybe originally I thought what do you meant, it's like either or.
>> It's not either or, right. And so one of the things we talked about in a different workshop together, was different types of state. There is your kind of your model, whatever the business logic is of your application, that stuff that a lot of would go into the context API in some cases.

Or in Redux. And then there's, yeah, is this form valid, right? You're not gonna put that in your Redux store. At the same time, it might have enough complexity that you don't want 17 sub-states, right, or your states in there, right? And so you're like, I just use one reducer that checks all the fields and figures out one list of all the errors.

So something along those lines, right? Every time I've ever grabbed a React form library, it is about a week before we throw it in the garbage. And just write a reducer that grabs all the values, tells us all the errors that we can unit test and call it a day, right?

And so yeah, for that ephemeral state, is this accordion open or closed? Is this form valid? I would probably use reducer every time, even if I had Redux in the codebase, right. I think it's a yes end. But as I was talking and typing at the same time, I just named it named instead of pact.

And so it's like, yeah. You can see that without a single useCallback, just, yeah, the React memos are still in there, without a single useCallback, I'm not re-rendering all those items and just, just to keep myself honest. You see that we get all of the performance gains that we had before.

So again, the major point here is that there is not one, you have to do it this way all the time, right? There are different trade offs, there's different things you could choose to do. If I had a nest that threw a whole bunch, right, maybe I don't wanna do that anymore.

Maybe yeah, hey, the act, maybe I gotta go 38 components. And sure, maybe it is more performant to do that, but there's a likelihood that in the next month, we're actually gonna have to move that box. Over there, right in a different part of the subtree. And you measure the context API, which we'll look at in a second, and had no memorization or whatever.

And it's still wicked fast. Yeah, do that, right. I'm not saying don't use the context API. I'm not saying don't use these things. I'm saying definitely think about it, definitely consider the options. Do the thing that just is React naturally first, right? Or at least consider that first before starting to cache and memorize things.

Cuz yeah, the second you're caching values, if you mess that up, you will get those weird bugs that you cannot recreate. And I say that as somebody who has lost days of my life, not that long ago. Embarrassing to admit how long ago, within the last month or two, to little things like that, because yeah, it seemed more convenient at the time.

And now I have these squarely issues, and it's just never worth it. And so it's worth considering all the options, what the trade offs are, you might still choose to use useMemo, useCallback, react.memo, absolutely. My job is not to tell you which one to do all the time, cuz there's no point.

I'd just give you a list and I'd walk out of here, right? My job is to basically, here are the considerations. All the things that could have been done by the compiler, the framework, whatever. The computer itself, the microprocessor have been done, we're left with the weird squishy trade offs that could go either way, that's in our day to day jobs the problems that we're left with, right.

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