React and TypeScript, v2

Using a Custom createContext

Steve Kinney

Steve Kinney

React and TypeScript, v2

Check out a free preview of the full React and TypeScript, v2 course

The "Using a Custom createContext" Lesson is part of the full, React and TypeScript, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Steve refactors the Color Theory application to use the custom createContext method.


Transcript from the "Using a Custom createContext" Lesson

>> So let's take this thing out for a spin, let's try it out. This is hard-earned, and so let's try it out. So now, [LAUGH] and you got to ask yourself, right, what's worse? And if someone's got a better solution, I'm here for it. But as far as I know, this is the way.

So what we wanna do is say that, we're gonna get rid of React. And am I so lazy that I'm gonna tempt auto complete here? Look it, I got it, cool. All right, so createContext, we'll say it's ColorContextState. Now, this one doesn't take a value. And it does return me a different, it returns to both these things.

So I have my own used context. So far so good, except for the fact that I got rid of some stuff. This one should still now wrap index.tsx, right? That should still be the same, one that we had before. So now we have to use our own useContext, let's see.

Try to have a more convenient way. Yeah, I don't. This is now we get to the points of opinions, and you can do whatever you want here. I wanna pull this out of the array and export it. I want it to be easy, I want it to be reliable.

I don't really wanna pull out the both cuz I don't need both all the time. So what I wanna say is I'll call this one useColorContext, and then export to useContext. I don't ever have to think about what the name was again, you could do the opposite, I don't really care.

I personally have waffled on this one, export const. If you'd rather use your special one here and keep the name inside, I don't care. So we can do this. We wanna get rid of the one we pulled in from React. And this one does not take an argument.

Cool, there are other versions of this, I have to go hunt down. This is now my bespoke one, I don't need to pull in the context all over the place, I don't need to pull in useContext. I have a hook that's entirely encapsulated with everything it needs to know, I can use this one from this point forward.

Yeah, I can pull the dispatch out. And are we ready for the big reveal? Does it work? Did he do all that work to find out that he has a bug? Everything works, so there's no more as. I did have to create abstraction, I did have to wire up my own bespoke version of createContext that is special to throwing an error.

And I don't disagree that that shouldn't be in the library, cuz maybe you don't wanna throw an error, maybe insisting. And forcing that on every JavaScript user is also a thing and changing the API and a breaking change. And if you've used React's Redux toolkit or anything like that, wrapping things with the more specific parts of your use case is a common pattern that you'll see a lot.

You get to choose which one you like more. If you're really against the casting as an as, that is fine. I think the reason to kinda talk about this one is it does pull together many of the subtle undercurrents of what we've been talking about using React. And I think the other powerful thing that you can do if you're like, yo, to useContext hooks, I'm gonna import the wrong one and it's gonna break.

I love your thinking, I agree, let's fix that. So yeah, this is gonna be confusing. A lot of the times, I want to build my particularly my state management code in my React app in a way that allows me to change my mind, right? What if I don't wanna use a context API anymore?

What if I wanna switch to Redux? Do I wanna redo everything in my app? No, I don't. So I could do stuff like, useHexColor, right? The name doesn't imply that I'm using a context API, it doesn't imply that I'm using Redux, it doesn't imply any of these things, right?

That's gonna do, let's say, magically I didn't export this one anyway, that this becomes an internal implementation detail that I never exposed to the world, right? And so now I could say, This one. Already, I'm confusing myself, right? And what I will do in most of the apps that I work on is create a series of these that hide what I'm using, right?

Now, throughout my app, I call useHexColor. I can call, Depending on how much I have going on, if there's a whole bunch of things, I can say I'll maybe make a use actions, right? Or if it's like use update hexColor, it'll just be a function that dispatches the right action, right?

There's a bunch of ways that you can do this, but what I'll frequently do is create it. I might choose to use this technique, but I want to hide this from everyone involved. I think one of the roles under the hood, it's electrons going through copper wires. It's a levels of abstraction to make things better.

One of my important rules when maintaining a large code base, that I like to enforce on my team, which is sometimes you have to do dark things. You put that in a file and you encapsulate it in one place and you do not let it spread through our code base, right?

Sometimes an API is not what we want, and we have to put an abstraction over it. Okay, that lives in one place contained, right? It's like every horror movie you've ever seen, you put that in the dungeon somewhere. You don't let the dragon out. And so you can encapsulate a lot of this.

And if you look at useHexColor, your hexColor is still a string, which has bitten me once today, but it's true. The rest of my application is type save, is aware of everything, it's not string or something else, right? Even the simplest version is to say useDispatch, right, which will, This looks like the one I was in libraries that we can talk about later, right?

That's it, that's the type, I will get dispatched out of there, right? And then I can use it everywhere. Everything we did is hidden. It's sounds like I'm now juggling two different createContexts, two different useContexts, or anything like that. I can create it up in this file, and then for the rest of my app, I can just use dispatch or I can even make more specific ones, right, and have those available everywhere.

Use adjust color and it'll give me the functions for doing that. I use those throughout my app, if I dramatically change my mind, right, then I have the option to just change my abstractions in this one place. And the problem with TypeScript, as long as I adhere to the same contract, everything is just gonna work, right?

And so it's one of the advantages that you get, is a compiler is gonna run around if you use these hooks everywhere. And it's gonna confirm that if you swap out this implementation for even the other one, right, that original one, nothing changes in the rest of your app, right?

If you swap out the context API for Redux, nothing changes in your app. If you swap out use state for use reducer, nothing changes in your app, right? And TypeScript will make sure that you didn't make a boo boo, almost immediately at lightning speed, to make sure that everything works, right?

And you get all those abstractions for free.

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