Using React Context (Now With Hooks)

Nov 1, 2019

3 min read

React Context has come a long way since its stable release almost a year and a half ago.

The context API is designed to allow state to be shared between components, and it's a great choice for storing things like a "global" setting (such as theme color) or data about the current user.

Now with the release of React hooks, you no longer have to use the "render prop" pattern to access state globally.

For this example we'll say that we have a current user in an app and we want to display their name in multiple places.

Setup

First we need to call React.createContext(), which will return a Consumer and Provider.

A Context Provider is a component that allows all components inside of it to access the shared state. The consumer is then used to access the data from the provider.

Usually it's best to put these all in their own file, so we'll call it UserContext.js

1import React, { useState, createContext } from 'react';
2
3const UserContext = createContext();

Next, we'll create a provider component we can use at the root of our app. We'll also give it some state to store the user object later, because our UserProvider is just another React component.

1function UserProvider({ children }) {
2 const [user, setUser] = useState(null);
3
4 return (
5 <UserContext.Provider
6 value={{
7 user: user,
8 setUser: setUser,
9 }}
10 >
11 {children}
12 </UserContext.Provider>
13 )
14}
15
16// you can also create this helper to avoid having to import two things to use this context
17const useUserContext = () => {
18 useContext(UserContext);
19}
20
21export { UserProvider, UserContext, useUserContext };

Here we set the value on the provider by passing it the value prop and giving it the data we want to make available to other components. We're also passing setUser, which will allow us to change the state of the provider from other components.

Now in the root of our app (usually something like App.js or index.js), we can add this provider:

1import { UserProvider } from './UserContext';
2
3const App = () => {
4 return (
5 <UserProvider>
6 {/* all the other stuff from your app... */}
7 </UserProvider>
8 )
9}

So if we had a login page where we were getting information about the current user from an API, we would want to store that in our UserProvider.

To do that, we use the useContext hook and pass it our UserContext.

1import { UserContext } from './UserContext';
2const LoginPage = () => {
3 const context = useContext(UserContext);
4 const { setUser } = context;
5
6 const handleLogin = async () => {
7 // get some data from your API...
8 const res = { name: 'Eleven' }
9
10 // and set it to context
11 setUser(res)
12 }
13
14 // the rest of your component would go here
15}

So now, if we had a navigation component that displayed the current user's name, we could do this:

1const NavBar = () => {
2 const context = useContext(UserContext);
3 const { user } = context;
4
5 return (
6 <nav>
7 {user.name} {/* => 'Eleven' */}
8 </nav>
9 )
10}