React Performance Using Multiple Contexts
Transcript from the "Using Multiple Contexts" Lesson
>> I would like to present my case, If I may, I'll let this sit on the screen for just bordering on what becomes an almost uncomfortable amount of time that doesn't feels like putting a cut in, but just long enough for dramatic pause. Does anyone see what the problem is?
[00:00:25] So Justin, so he's about to hit the button, all right. On the fly, at the very last minute, we're taking these two things and we're putting them in a brand new object. That brand new object, it's not the same object as it was last time. Items might be the same, dispatch might be the same, right?
[00:00:54] But this object is brand new, which invalidates all of those checks in React.memo. And some things just care about dispatch, right? Before items, by themselves, they're like, yo, if I'm getting the same item I got last time, even if the list is all new, I'm good, I'm cool.
[00:01:20] Dispatch is the same, getting the same item, right? But here, technically, it is pulling dispatch off of a brand new object, right? The ES6 syntax hides it from us, but the rules are being followed once again, right? All of the initial rules that we set up in the very beginning are true, and we just opted out of everything.
[00:01:42] While also still having those React.memo checks, and they're doing nothing now, right? They're useless, they might as well take them out. So, does anyone have hypotheses on how we would solve this, Alex.
>> We just create object ones above return, destructure in there and then just pass the dot reference?
>> But the problem is, items will change. So we'll have to make a new one every time. Dustin.
>> Memoize provider value?
>> Memoize provider value, but then I might get an old, but that'll get broken by the items changing. And even for the individual item that only cared about poor dispatch.
[00:02:21] Dustin, well, I think you're reading from the chat room, but you're gonna get credit because I-
>> Two contexts?
>> Yes, so one context caused our problem, do you know how we solve it? I heard you like contexts. So I put context in your context. Why didn't I make that meme and have it on a slide ready to go?
[00:02:39] One context created our problem, two contexts is our way out, right? Cuz we don't mind things rerendering when things change, that is what is React is supposed to do. That's why it is rerendering, because things change, that it has to put. Rerenderings are not bad in and of themselves, they're just bad when we don't want them.
[00:03:02] So things like the items list changing when the items change. Okay, right on, the items changed, please show that in the UI to me, love it. The things that just rely on dispatch, well, that seems silly. So yes, the answer to all the problems that one context solved, it's two contexts.
[00:03:23] One for the things that change, and one for the things that don't change, right? If that hurts your feelings, that's good, yeah, that's valid.
>> So by then, we will need to wrap each individual component with the context that the one that changes to prevent the same thing, or no?
>> Well, so we already have this abstraction here that is taking one context and wrapping children, which is the whole application. And are you all familiar with the turducken, right? We can just keep it's a turkey, a duck, and a chicken, all in one dish. And what we can do is basically take the same approach, And try some dark move like this.
[00:04:12] I don't even know what they name stuff anymore. ActionContext, cool, cool, cool. Now for the items, That's just gonna be passing through. And then what we do, This is now a static value that is never gonna change. Right, and so now the important tasting note that I wanna call out is the order is important, right?
[00:04:56] Because all parents trigger rerenders on their children, right? So we're gonna put the one that doesn't change, Outside of the one that does change. That's the same way that we pushed down the changes earlier. We're doing that here now, effectively, which is the one that doesn't change is going to be inside of the one that does change.
[00:05:19] Yeah, could you memoize them? Yes, I can, I don't want to, right? That doing stuff as fast as I'm doing stuff. So we've got those in place. And now I have two, so now instead of useContext, if you need the items, you use the ItemsContext. And if you need the actions, you use the ActionsContext, right?
[00:05:45] And let's try it out, and we'll go into the individual item. Now we should be able to just swap this one out, at least. And pull that in. What you're mad about? I mean, there's plenty to be mad about. Header's mad, I don't really care. Cuz they're pulling on, they're deconstructing object before, so I can't just change it in one place, that makes sense.
[00:06:14] This is now just pulling out the regular old items. And did I see, only other place, yeah. Dispatch is not a function, I left also the deconstructing here as well. The express purpose was not to make new objects, so lets falter. And you look, all of my performance gains are back, but at what cost, right?
[00:06:47] We can make it a little bit better, right? I have a rule, which is, sometimes we have to do bad things. And what do we do when we do bad things? We cover them up, [LAUGH] right? If you do bad thing in your code base and you cover it up, it's okay.
[00:07:05] I have bad news for you, there's electrons going through copper wires in this thing, right? This is all [LAUGH] built on top of a pile of lies and obstructions, so obstructions are just lies, right? And so that's what's happening here, it's just complexity of electrons, I don't even know how Wi-Fi works, honestly.
[00:07:23] So all of that, and so what I would do in this case, if that hurts your feelings, you need two different contexts, and then have to keep track of this. And if you have a suspicion that this is going to get out of hand, of course it is, [LAUGH] right?
[00:07:37] It's absolutely gonna get out of hand. So what I do usually, and I would highly recommend this, and where you put it, I don't really care. Which is, you cover the bad thing that you did up, right? UseItems, And useItems is great. useItems is gonna do useContext, it's gonna take the ItemsContext, Return those items, and we'll export this.
[00:08:16] And then we'll go ahead and say, The records show that I can type, it's just I don't normally stand and work like this for several hours in front of people. So here we'll say, const dispatch. = useContext, that ActionsContext. Let's say we can do all that, that should do the trick.
[00:08:52] I don't need to wrap this one to use callback cuz it is the same value. Just doing that might make me feel good, but it should be the same value, no matter what. And so, to say make sure this is the same value when it's already the same value, is not doing anyone any favors.
[00:09:09] This one, I'm a little more on the fence about. Because it's almost always gonna change if it ever gets called, it's like the opposite problem. This will always be the same. And this one is the literal only [LAUGH] business logic thing in my app. So I could wrap it, and it will always get invalidated, right?
[00:09:26] Like two sides of the same coin. And so now, I can go inside my application, and we'll swap this out. This is not a performance talk anymore at this moment. This is simply hiding the stuff you did in the name of performance from yourself. Right, and so now we've got that in place, and now in the individual item.
[00:09:55] Did I export the second one?
>> I think was useDispatch.
>> useDispatch, no. [LAUGH] That means the thing. There's a reason why I should do that, that's smart, I should do that. The literal words that I was about to say. The nice thing about doing this is that if you ever decide, the question before is like, at what point do I go from useReducer to Redux, right?
[00:10:19] Now that I've given myself these abstractions and these hooks, whenever I want. Cuz I would just change one file and get all the new ones. This way used to especially be good, cuz React Redux has that useDispatch thing. And it gives you a dispatch, that would have been smart, I should have done that.
[00:10:35] And now it doesn't take an argument, or if it does, it ignores it. And so, if you need to do this, and again, now, to be clear, if the problem with using the context API was we lost all those, the rerendering support. Well, we got it back, and the cost of getting it back was complexity.
[00:10:57] We did just hide the complexity in one place, right? I don't know, like, look at the React source code. There is complexity lying around, and it was just hidden from you, you didn't have a problem with until you saw it, right? So here's the thing that you can do.
[00:11:09] And again, it's all sort of trade offs. Now this is a thing that you own, [LAUGH] right? And so, yeah, that is there as well, and a thing to consider. Was one question here. Okay, can I show the profiler now? Yes, I can. And then there was another question.
[00:11:24] Let's look at the profiler. Let's do some recording. And we'll move some things. And then, I keep right clicking for some reason. So you can see that they are not rendering. These extra stacks are my new providers, which are in existence, right, totally. But you can see that the kinda core piece here is fine, right?
[00:11:52] And less than 0.1 ms, 0.1 ms, I am okay with that, [LAUGH] right? It's not like stop renders at all costs, right? It is about making sure that you don't have more than you need. Sure, but I gained not having to pass stuff through. I can probably take the React.memo off of the items list now.
[00:12:20] Since it's only ever getting the items which are changing, it's not getting needless things pass through it anymore. So yeah, I got some contexts now that are rerendering. Sure, seems good, I'm into it, right? And so I think that that works, okay. One glance to the chat real quick.
[00:12:42] So a question on your question that you've passed children to component, the children calculated first. How would they be able to access context values when they're getting calculated before the context provider? Do they fall back to the default context values? I mean, they get updated, right? There's the very first time where they might get the first value.
[00:13:03] But they will quickly, when those state changes happen, they are hooking into that prop. It is like they are reading from it at that time, and getting those updates. Cuz that is, again, it's that secret prop thing where it is a prop is passed to all of them through this API that runs kind of aside the normal hierarchy tree.
[00:13:21] That's why the context API exists, right? It's to kinda give us that thread that it binds all components together. It's like the Force, but for components, and triggers rerenders, that's not what the Force does. But it will then, those things are what triggers those updates and renders from that, cuz it counts as a prop.
[00:13:42] And so, it being its own special API does have some special rules around it. Another question, do we need to put the provider with a context that won't change over provider with the context that will, and why? So I don't really know. So it was one of the things I was thinking about, which is, it is a thing that I did the very first time cuz I have always done it.
[00:14:04] So the question is, I said, put the one that doesn't change outside first, right? But then I was thinking about that chat we just had about children get pulled up. I was like, have I just been doing that for the last several years? Because I thought it in the year 2018 and have just done it every time.
[00:14:25] The order might not matter as much as I thought, as I was saying those words the very first time out loud to people. Because yeah, we talked about the content gets pulled up and they might render. But it's definitely worth the experimentation.