useContext for Global State

Ben Hessel
6 min readJul 20, 2022

--

Along the journey of learning React Hooks and developing my Netflix Movie Matcher app, I reached a point where I started to implement a login.

And as you’d expect… You need the User data all over!

You don’t want to bloat your app with props being drilled from the top parent component to the final child components, so what do you do?

Solution: React’s built in useContext hook allows us to create a global state wrapper around every component that may need access to that state.

Ahh… so smart!

In the following example, I built my AuthContext file with the useContext hook, to allow a User to signup, login or logout, and have those functions (plus user data) available anywhere in my app.

Instead of prop drilling user data down from App.js to children and grandchild components, our Context file here actually saves the current user “around” all of the other components it wraps.

Like other hooks, useContext has a pattern for you to build around.

In the rest of this post I’ll give you a framework to build from, so that you can implement useContext for your own needs in your app.

First, we set up a context file. In my example, this is called AuthContext.js .

Then we create a variable named AuthContext (whatever you want really) and set it equal to createContext();

(You also need to write => import React, { useContext, createContext } from ‘react’ at the top of the file)

Using more English and less Computer, we can say we’re essentially creating a variable here from which to call context (global state) by, and build our wrapper with. In this case, that’s AuthContext.

Next, we’re going to create a function called AuthProvider. This will be your export default, and it takes in children as props.

As you’d probably expect, children just refers to the components below the component where you’re applying your Context wrapper.

The AuthProvider function will essentially take place of the AuthContext.Provider that we’ll return up in a minute.

Now, inside of the AuthProvider function (remember, this is going to wrap our whole app), we need to store user data.

So first, we’ll import the useState hook.

In my code I had set this up with a custom hook called useLocalStorage to make sure my user persists, but for purposes of this tutorial, just use useState.

As you’ll see here, I also have functions that allow the user to sign up, sign in, or logout.

You can make your own!

Then, everything I want available to me throughout various levels of my App, I put into the value variable on line 88.

If you were doing a light/dark ThemeContext for example, you’ll be storing different data in your ThemeProvider than I am in my AuthProvider *

*(ThemeProvider would = AuthProvider in that case)

This includes all of the functions mentioned above, plus the currentUser state, and the function to setCurrentUser .

You’ll also see that on line 96 is where we have our return function, and all we’re returning from the above code is the AuthContext.Provider with its value variable, and children rendered between the provider.

The Provider basically just passes the data to all children in the tree below it.

If we were to refer back to the ThemeContext example for a second, it would be ThemeContext.Provider , or whatever matches your variable from line 6.

Again, as we walk through this, keep thinking ‘Global State’ when you think useContext.

So, how do you access one of these functions (say… setCurrentUser) lower down in the app?

The last function on in our AuthContext file will be the useAuth function you’ll find on lines 8–10.

Here, we pass in the value from our context (AuthContext) to useContext() and return it.

In plain English, this basically says “hey, import this function to other components to gain access to anything you passed into your value prop.”

Elsewhere in the App I have a Signup file and a Dashboard component. These will do for the purposes of this demo, and for giving you a foundational setup to build your own context wrapper from.

Now, as I take you over to index.js , this is really simple:

Import the AuthProvider and create a an AuthProvider component that wraps around round the App component:

~Voila~

I have React Router V6 imported, you’re welcome to do the same, but React Router isn’t going to be my focus here.

As we go down to App.js, you’ll see I have a home route (/ ) which sends me to the Dashboard component, components to Signup and Login , and a VideoContainer component that I’ve linked to from Dashboard .

The PrivateRoute I’ve implemented is to make sure that someone can’t access the logged-in pages before they actually log in (else they get navigated to /login), but again, not a focus of building out the useContext hook!

App.js

So we’re going to start at Signup.js where I just have a real simple login form.*

*I’ve cut out the return portion because I trust you know how to make your own login form if you’re learning this hook :)

Dashboard.js

Where you need to focus here is highlighted on line 11, and of course you need to import this useAuth function built from AuthContext.js (line 2).

Syntactically, this pattern is pretty similar to when you take in props from a parent component, and de-structure them to write cleaner code in the child component.

Use the framework of const { } = useAuth() , and anything you need from the value prop we passed in will be accessible to you (e.g. loginUser, currentUser, setCurrentUser, etc.)

Now if we look at our Dev Tools, you’ll see that there’s no User state yet:

No User in the LocalStorage hook. If you used useState, you’ll see the same — no one is logged in.

So we’re gonna sign up a user I’m calling contextDemo@react.com .

Our submit function navigates us to the Dashboard component (logged-in home), where we can see the user we just created!

If you look back at App.js , we never passed this state prop down in a traditional pattern:

What we did once again is import useAuth , set the state and/or function(s) we need from AuthContext (via the value prop) on line 7, and then I displayed the current user’s email on line 26.

No matter how far down a component tree you go, you can use this same pattern to pull in props from your Context provider and have it set state globally.

--

--

Ben Hessel

Aspiring Software Engineer, Student at the Flatiron School