- Now the last hook that I want to talk about
inside of React and TypeScript
is going to be the use context hook.
And this one requires quite a bit of setup
just because of the way the use context hook works.
So the very first thing you'll notice
if we have this code here, everything's working fine,
but I get one error right here and it's essentially saying
that it's expecting a default value.
And that's because create context always expects you
to pass in a default value.
But oftentimes you don't have a default value.
If you're creating a theme context
you can obviously pass in a default value of dark mode,
for example, but oftentimes your context
is a lot more confusing than this.
In our case, our context is getting an array of users.
So what we could do is we could pass in and say
that our users by default are going to be an empty array.
But let's say that we did not want
to pass in a default value for our users.
We want it to be a little bit more complicated
because it involves a little bit more data.
Oftentimes what you're gonna do is you're just gonna
pass in null as your default value because you want
to be able to populate all your information inside of here.
For example, maybe it's gonna be a bunch
of different functions that you want,
you wanna have an add user function and stuff like that.
And you obviously can't define that up here.
It must be defined inside of your component.
So that makes it a little bit more tricky for you to do.
For example, if we just create that function,
we'll just say, "Add user."
It's going to be a function, add a user.
And this add user function,
we'll just say it takes in a name.
And what it's just going to do is just going to set users
based on the users that we have previously.
There we go.
Return. And we're gonna get all of our previous users.
And we want to add on a brand new user,
so we'll get a random ID for them.
There we go.
Our name is going to be our name
and also it should probably take in an age as well.
That way they at least match all the same stuff.
There we go.
And this is going to take in a name,
which is a string, and an age, which is a number.
There we go. So obviously I can't do a default value.
I could say that my user is gonna be an empty array,
but I obviously can't define my add user section as well
to be able to get that default value properly.
So what I need to do is just pass in null.
That's probably the best thing
for us to do in this scenario.
And then what we can do inside of here is we can define
what we want the type for this to be.
So we can create a type for our context.
We'll just say "Context type."
And we know that this is going to be a user array.
So we can say we have a user array.
And we need an add user function.
And this add user function takes in a name and an age,
which in our case is going to be a string,
and an age, which is a number, just like that.
And then it's going to return to us nothing,
so we can just keep the return type as void.
There we go. That's our context type.
Let's pass that in there for our create context.
Also, since we're setting our default value to null,
we need to make sure that we specify null here
as the other type, so this context right here
is a type of either our context type,
which has all our information, or of null.
So that's really good.
And you'll notice now all of our code
inside of here is working fine.
So really the only thing you need to do when you're creating
your context is you need to give it a generic type you want
and you need to give it a default value.
And if it doesn't make sense for you
to supply a default value, for example,
if you have functions and stuff you're defining,
my recommendation is just pass in null and then
just make sure you can have it optionally
be null as the other type.
So now it's either going to be our type
or it's going to be null.
Now the one problem, though,
is this doesn't solve all of our errors.
If we go here to where we're using this,
you'll notice here we're getting an error
because users does not exist
on the type of context type or null.
And that's because it could be null.
And in our case, if it's null, obviously there's no users.
So generally what I like to do when I'm using context
is I like to create a custom hook.
So we can just call this hook whatever we want.
We'll call it "Function use."
And I generally, I'll say like use users,
if that's what we're doing in our case.
And then I no longer need to export my actual context
because here this hook is going to call use context
and it's going to pass in my context just like this.
And that's going to give me my user's context.
And you may think, "Well, I'll just return that as is."
But the problem, again, is that this can be null.
So what I wanna do is I wanna just do a really quick check
to see if my user's context is equal to null.
'Cause if it is equal to null,
well, now I can just throw some type of error.
So I'll say "Throw new error.
Must use within provider."
There we go.
And then down here below that I can return my user context.
So now if I look at this, the type of this down here
is actually just my context type because I check to see
if it's null, don't let me return it at all.
So now if I use this use users hook instead,
just like this, make sure I import that properly,
get rid of all these imports up here.
Make sure I actually export it from here so I can import it.
There we go.
And now if I try to import that you'll notice
all my errors go away, because now this user type is defined
because this is returning to me
whatever my context type is in my case.
So again, to kind of reiterate what we've done here,
is essentially we've said. "Okay, we created our context
and we specified the exact type we want it to be."
But since we need to pass it a default type
we're gonna say, "You know what?
By default we're gonna set this as a null."
And that means if we try to use this context somewhere else
where it's not wrapped inside a provider,
'cause if we wrap it inside of a provider,
the provider always has a value.
It'll never be null if it's inside of a provider.
So this will obviously never be true if we're in a provider
and it'll just return our context with the correct type.
But if for some reason our child is rendered
outside of our provider, well, now if we make sure
we just wrap this properly, there we go.
Now we're actually going to get an error.
And if I inspect my page, go to the console,
we'll essentially get an error, "Must use within provider."
And that's because again, this is returning to us null
because we're not in the provider.
So it has no value.
So our user context is set
to the default value, which is null.
So that's what this check right here allows us to do.
Obviously, though, if we want to have this in the provider,
which is what's going to happen most of the time,
it's not going to be null, which means
it'll just return to you your user context.
And since we put this check here, it's going to make sure
that it's never null inside your type.
'Cause we already checked to see if it's null,
which means when you actually use it
you don't have to worry about that null error check at all.
This is really nice because now you can use your context
and just assume everything is going to be there,
which is really great.
And over here, inside of your actual code
you're doing all the different null checks and stuff
that you need to to make sure that
your actual consuming code is able to consume it
without dealing with all that behind the scenes.