Understanding the Context API in React

Understanding the Context API in React

The context API is an easy and efficient way to manage global state in react. Understanding what context is and how to use it can help in building more robust react apps in an easy and efficient manner.

What is the Context API?

The context API is a way through which developers can maintain data or global state in a single location and share this data/state to the various components that need access to them irrespective of where they are in the component tree. The context API helps prevent a situation known as props-drilling where data would need to be manually passed as props, many component layers down, to get to where it's actually needed. It helps keep code clean, readable and understandable.

Putting Context in Action

To better understand context, we will be building a small hello world app with light and dark themes. The theme will be the global state we need to share to the various components in our app.

Creating the Project

Since this is a react tutorial, we'll need a react project to work in. I will be using vite in this tutorial since it's a lot faster than "create-react-app" but feel free to use whatever you prefer. Spin up a terminal window and type the command "npm create vite", input a project name, pick react as your framework, pick javascript as your language, run "npm install" to install dependencies and finally, "npm run dev" to start up a local development server.

If you're new to vite and would like to get a more detailed explanation of getting up and running with Vite, you can check out the Vite docs.

With the project ready, it's time to start building our app.

Creating the Template

Our template will be quite simple. We want a div at the center of the screen with the text "Hello world!" and a button, that when clicked, toggles between the light and dark theme.

To get started, first delete the App.css file as we'll be using the index.css file for all our styles. In the App component, clear out everything in the return statement and replace it with a div with a className of App, also delete the imports at the top as we won't be needing those. Your App component should now be looking like this:

function App() {

  return (
    <div className="App"></div>
)
}

export default App

Next, we'll create a component to display the "Hello world". Create a new file in the src directory and call it "HelloWorld.jsx". In the component, add in the following code:

const HelloWorld = () => {
  return (
    <div className="hello-world">
      <p>Hello world!</p>
      <button className="toggle-btn">Toggle dark mode</button>
    </div>
  );
};

export default HelloWorld;

Import the component into the App.jsx file. Your App component should now be this:

import HelloWorld from "./HelloWorld";

function App() {
  return (
    <div className="App">
      <HelloWorld />
    </div>
  );
}

export default App;

If we preview our app in a browser, it looks a bit boring. Let's add some css to address that. In the index.css file, clear out everything and paste this in instead:

.App {
  min-height: 100vh;
  display: flex;
  justify-content: center;
  align-items: center;
}

div.hello-world {
  padding: 2rem;
  border: 2px solid black;
  border-radius: 16px;
}

p {
  font-family: sans-serif;
  font-size: 2rem;
}

.toggle-btn {
  padding: 1.5rem;
  border-radius: 16px;
  cursor: pointer;
}

/* The dark class we will toggle on components */
.dark {
  background: black;
  color: white;
  border-color: white;
}

Our app still looks terrible but it has a lot more going on. Your browser should now be looking like this:

With the template ready, let's get into making that toggle button useful.

Creating Context

Contexts are usually created in a separate file. In the src directory, create a new directory and name it contexts. In the new directory, create a new file and name it ThemeContext.jsx.

Context works by storing variables and functions that modify these variables in a single file and passing them as values to a "context provider" so they can be accessed by all components that are children of the provider irrespective of their position in the component tree. It may sound complicated but the process of creating one will make everything a lot easier. Contexts are created by using a createContext function from the react library. The function takes as an argument, the initial value of the context. We will pass in null.

import { createContext } from "react";

export const ThemeContext = createContext(null);

We export the context so we can use it in other files. And that's about it, we've just created a context that we can use in our app.

With the context created, how do we use the thing? Contexts are used by wrapping all the components we would like to have access to our context in a Provider component. The provider has a value prop which specifies the value of that particular context when accessed by any of its children. Let's give the ThemeContext a string value of "I am a react context!" Modify the App component's return statement to this:

function App() {
  return (
    <ThemeContext.Provider value="I am a react context!">
      <div className="App">
        <HelloWorld />
      </div>
    </ThemeContext.Provider>
  );
}

Remember to import the ThemeContext at the top of the file like so:

import { ThemeContext } from "./contexts/ThemeContext";

It's now time to use the context in our hello world component. The easiest way to consume context in react is by using the useContext hook.

Consuming Context Using the UseContext Hook

In the hello world component, we'd like to log the value of the theme context to the console, just to make sure everything is working as it should. To do that we'll use the useContext hook. UseContext is a hook in react that helps us consume context in our applications. It accepts as an argument, the context to be consumed and returns the value of that context. We'll now pass our ThemeContext as an argument to useContext and store the return value in a local variable we will conveniently name localVariable.

In the hello world component, above the return statement, add this:

const localVariable = useContext(ThemeContext);

console.log(localVariable)

Don't forget to import the context at the top of the file like so:

import { ThemeContext } from "./contexts/ThemeContext";

If we save our file, return to the browser and open the console, we should see the string "I am a react context!" logged.

Now that we know how to create and use contexts, let's kick things up a notch and try using the context to do a bit more than pass a string around.

Using Context to Hold Global State

For this project, the state we want our context to hold is the app theme. Let's create a state to hold this in the App component:

const [isLightTheme, setIsLightTheme] = useState(true);

To make things easier, the theme state is represented by a boolean, isLightTheme. With the theme created, we'll replace the value of the ThemeContext from the string "I am a react hook" to an object with two properties: the isLightTheme value itself and the function to set it.

<ThemeContext.Provider value={{isLightTheme, setIsLightTheme}}>
      <div className="App">
        <HelloWorld />
      </div>
    </ThemeContext.Provider>

Now, we can access these two values anywhere in the component tree. In the HelloWorld component, we'll destructure these two values. Clear out everything above the return statement and replace it with this instead:

const { isLightTheme, setIsLightTheme } = useContext(ThemeContext);

With the theme now available in our hello world component, add an onClick handler to the toggle button that toggles the light theme true or false like so:

<button
        className="toggle-btn"
        onClick={() => setIsLightTheme((prev) => !prev)}
      >
        Toggle dark mode
      </button>

We can now change the theme from light to dark, only issue is, we can't exactly see our changes, let's fix that.

Conditionally adding the dark class

In our css at the beginning of this tutorial, we created a dark class. We'll add that class to all our elements whenever the theme is dark and remove it when the theme is light. We'll start with the App component. Modify its return statement to this:

return (
    <ThemeContext.Provider value={{ isLightTheme, setIsLightTheme }}>
      <div className={isLightTheme ? "App" : "App dark"}>
        <HelloWorld />
      </div>
    </ThemeContext.Provider>
  );

Then modify the HelloWorld component's return statement to this:

return (
    <div className={isLightTheme ? "hello-world" : "hello-world dark"}>
      <p>Hello world!</p>
      <button
        className="toggle-btn"
        onClick={() => setIsLightTheme((prev) => !prev)}
      >
        Toggle dark mode
      </button>
    </div>

If we save all our files and go back to the browser, clicking the toggle dark mode button now switches between light and dark modes!

Conclusion

This article has given you a short introduction to using the context API in react. Contexts are extremely useful for holding global state in our apps. I hope the tutorial was easy to follow along, thanks for sticking to the end and happy hacking!