React and TypeScript, v2 Context API
Transcript from the "Context API" Lesson
>> Now, a better way is if I had done this with the Context API, I could just have the thing itself be able to dispatch that action with its own onclick. We should do that, that sounds good, right? But I'm gonna teach a workshop on React performance later.
[00:00:21] And the Context API has some implications that are sometimes not great, and we'll talk about how to get around those. But the Context API, all the time, every time, there's a really great blog post by Mark Erickson where he talks about how the Context API is not a state management tool.
[00:00:37] And that's not the theme of our workshop today, but that's the thing to go read, if you really want, and I somewhat agree. So we've got that in place, let's actually look at implementing the Context API in here, as well. So I'll make a new file called context.tsx, put it in the root directory.
[00:01:02] For those of you who don't know the context, there was an old API, Legacy Context API, which gave any component anywhere in the hierarchy tree, the ability to reach in to this shared tree. It was not a stable or documented API, but libraries like React Redox and stuff like that used it.
[00:01:22] It's one thing for a library that has it in fully supported to use, is nothing if you just wanna use a code base, and then the Facebook team wanted to change it. But now there is a real Context API, and we say createContext. And this is where it gets tricky, give it a default value.
[00:01:44] Well, we've played this game before. We've played it with useState, we've played it with useReducer, we've played this game many a time before. So I could be like, here's a hexColor, right? And maybe it starts out as I'm not gonna use that special type cuz we didn't really implement it.
[00:02:07] And so I could say, ColorContext, and it knows that ColorContext has a hex or has a string, right? Cool, cool, cool, and at first, life seems great. Life seems good, right? We'll go ahead and we'll export const ColorProvider. Let's also, cuz I know that I'm gonna forget, export ColorProvider.
[00:02:43] And that's gonna need children, but we know how to deal with that. We'll deal with that in a second. And we kind of wrap this we say, ColorContext.Provider. This is just a thing. This is not a TypeScript-specific thing, just yet. We'll give it children. And we'll say that this is PropsWithChildren.
[00:03:07] But then we need to actually Declare that, as well. Let's see, Sometimes you make the same mistakes so often that when you see it you're like, I don't even need to read the error message cuz I do that all the time. So we have that in place, and so now it's like, yo, I need a value.
[00:03:42] So the default value was fine, but this one we use it like this we have to give it a value. And if you ever look at the, what was Mark's favorite one, again? There we go. If you ever look at the documentation, and I believe this is still true, I've not personally pulled up the Context API documentation, the example is, there is a theme, right?
[00:04:12] Object of the different colors or whatever. And they pass in the value, and then that is available to everything. The docs there's like, I believe this is still true, somewhat silent on state management in here, and yet, we all do it. Even when I said we should use the Context API, I saw a bunch of people nodding, right?
[00:04:30] Yeah, we should, but I guess we all do it. And we gave it a default value, so it figured it out. We also wanna give it dispatch, right? But I can only use reducer inside of a React component. And if I wanna export this ColorContext, I can only do it outside of a React component.
[00:05:02] These two things are fundamentally different, right? If I try to do something like, yeah, yeah, check it out. Well, it's getting the type, so we can do the same trick we did before. We're like, yeah, I think you're not gonna pull one over on me, hexColor is a string.
[00:05:22] It's happy with that cuz it's like, cool, you gave me an object where the hexColor was a string. I'm like, cool, cool, cool, Dispatch, and that's gonna be supported in, ColorAction. It's like, yo, give me a dispatch. The dispatch is ColorActions. I don't have one until over here, inside this closure scope that I can't escape.
[00:05:54] What am I gonna do? I got options, none of them are great. I will eventually get you to a better but more confusing option. But I've two options here, both are not great. I can do this, but guess what? Now the dispatch can either be Dispatch<ColorActions> are undefined, I will forever live with this.
[00:06:23] Which is super annoying cuz I am going to define it five lines from now, right? And since this is going to, this code will run, this will wrap my entire application component, there is very little chance that there will ever be a world where dispatch is not defined and we're using this context.
[00:06:46] I'm sure you can create some weird edge case, it can happen. I'm not gonna say, no possible way, but yo, I'm gonna define it before it even gets to application, good luck using it before it's defined. And, yet, then I have to forever live with the fact that it's an optional, that's not great.
[00:07:07] It's correct, but it's not great. The other option is to sell TypeScript to chill, right? And, again, Michael in the chat commented what that is, when we're saying, this is a hexColor. With great power comes great responsibility, right? You better unit test those functions, because if you're gonna insist that something is so tight, you better be right.
[00:07:34] We have another option here where we're gonna be like, okay, yeah, it's not that time, but for a minute, pretend it is, right? And this is less dangerous than an cuz it is isolated to one place, it doesn't affect everything else. But it should be treated with the same amount of concern, which is, let's actually make this a type.
[00:08:10] Just trying to make sure I didn't pick one that was already taken. I don't love that name, but it's good enough. And it's like, well, that's yeah, that's not the right type, but pretend it is, right? And that will work. And if you feel bad about this, I will say two things to you, valid.
[00:08:32] The second thing I will say to you is, let's look at literally what Create React does to bootstrap your entire app. Because, theoretically, document.getElementById('root'), there could not be a root on there. So getElementById returns either a or undefined. Now, anyone setting this template up understands that they made the public.html, there is a root on there.
[00:09:02] So they're saying, your type should chill. I know you don't know, but I know. So, valid, and we will actually get rid of this in a little bit. So if you're like, yeah, what is he doing? Don't worry, also, I have [SOUND] never been bitten by this one, yet, but we also will solve for it.
[00:09:24] But that is one way to handle this. Now it's all yelling at you like, you didn't give me a dispatch over here. So you know that this is not infecting other things in your code base. This is a one-time, trust me, I know what I'm doing. I can see the chat moving.
[00:09:37] Yeah, nobody likes this. Anyone who feels good about this, I mean, good for you. But yeah, I agree. And we'll look at another way that is a little bit more complicated, but also kind of like solves this problem. But this is, it's the chicken and the egg scenario, [COUGH], and something we kinda have to deal with.
[00:10:00] So then I can pull this fun stuff, let's leave it in there for a second, and we'll move it into the context, as well. We'll grab useReducer. Here we just need to pass in that dispatch, as well. Awesome, and so now this all wires up and it works, and we can use this context anywhere.
[00:10:37] So I will initially put it in all those buttons and we'll move it into the app real quick. I guess, yeah, we don't wanna hard code in that anymore. So pull the one from state, cool. And here we'll say, Notice that I forgot to wrap my index.tsx in this, so somebody hold me accountable to that in a moment before I get confused why nothing's working.
[00:11:11] So here we only need to pull out, I guess I could pull out, yeah, I'll pull out both the hexColor and the dispatch here. Because that way I can just put the hook in the new place, and I don't have to put it everywhere cuz I did some of that threading earlier, so.
[00:11:30] Just limiting the amount of refactoring I have to do at this moment. Cool, so we've got all that in place. I'm just gonna validate that I have not broken anything before I go, remember, someone has to keep me accountable, I'll put in index.tsx. We do need to wrap our application in this.
[00:11:48] What did I name it, ColorContext or ColorProvider? ColorProvider, Awesome, and so now let's validate that it works, cool. So now wiring up the rest of these buttons or honestly, these input fields should be a lot easier at this point. And so let's do it for the color buttons, color swatch buttons.
[00:12:18] ColorChangeSwatch, so that had the onclick. I would say, if nobody gave you one, we'll use the one that I just wrote. Really, in this case, all we care about is dispatch. UseContext with ColorContext. You know what? Let's get rid of this, I'll do one with a refactor, but it's easier than putting a weird condition on there.
[00:12:59] So on here, in this case, what we'll do is we'll say, dispatch. Again, love to see it, every time it makes me happy, every time. That didn't need to be an object, though. Awesome, and so now, theoretically, All these work throughout the code. I lost it. One of them had a name.
[00:13:40] Anyway, so now they all exist. It all works, everything's great.