Tech, React

How to Replace Redux with Context API

Redux is one of the most popular and powerful state management tools to React that enables handling the shared application state.

However, it is not that easy to understand the process, especially for beginners. You need to be familiar with concepts like central store state, reducers, and actions along with other higher-order functions.

The good news is React has introduced a new feature that includes React Hooks and Context API. The availability of these two features simplifies a lot of problems for developers coding large React projects. The Redux mechanism where it creates its own set of the project structure in order to enable it to the React project, becomes more simple with React Hooks and context API. How so?

  1. Sharing of the state becomes easy between the screens as well as nested props without having to dive into the complex structure of Redux.
  2. We can simply recreate the Redux-like structure in the React project without having to use Redux packages. It greatly reduces the complexity of the state management structure to be applied to the React project.

What is Context API?

React Context API is basically a way of creating a global state or variable.

The created state can be easily passed to different screens and components in a React app. This eliminates the need for 'prop drilling' where parent components have to pass the props to the child in a nested component structure.

It is a lightweight solution to the complex Redux state management mechanism. One of the advantages is that the required hooks and context API is provided by React itself without the need for any package installations.

State management using Context + Hooks

State management using Context API and Hooks is simple and easy.

Here, we are going to create a simple React project where we are going to demonstrate the use of React Context API and Hooks and change the Theme of the app. The idea is to set the theme of the app using React Context API and also toggle the theme.

But first, we need to create a new boilerplate React project. For that, we can go ahead and execute the following command in the desired local directory:

npm create-react-app contextAPI

Configuring Context and Provider

Now, we are going to make use of the Context API and React Hooks. First, we need to create a file called ThemeContext.js inside the ./context folder in the ./src folder. This file will act as a central context store for the global Theme data.

In ThemeContext.js file, we need to add the following code:

import React, { createContext, useState } from "react";
export const ThemeContext = createContext();

const ThemeContextProvider = (props) => {
  const [state, setState] = useState({
    isLightTheme: true,
    light: { syntax: "#555", ui: "#ddd", bg: "#eee" },
    dark: { syntax: "#ddd", ui: "#333", bg: "#555" },
  });

  return (
    <ThemeContext.Provider value={{ ...state }}>
      {props.children}
    </ThemeContext.Provider>
  );
};

export default ThemeContextProvider;

Here, we have created a context object called ThemeContext using the createContext() instance imported from React.

Then, we have defined a Provider function called ThemeContextProvider in which we have defined a theme state called state using useState React hook. The provider function returns the Provider component from ThemeContext holding the state as a value prop.

Finally, the function is exported. The ThemeContext instance is also exported.

Once we created the context, we have access to two components: the Provider and the Consumer. The React Context Provider enables all the components in the project hierarchical tree to have access to the state data in context. Hence, consume the context data. But first, we are going to create a view file in which we are going to make use of the states in the ThemeContext.js file.

Accessing context variables inside a View

For this, we need to create a file called MovieList.js in ./src directory. Inside, we are just going to add a simple list of movies. Here's the code for it:

import React, { useContext } from "react";
import { ThemeContext } from "./context/ThemeContext";
const MovieList = () => {
  const { isLightTheme, light, dark } = useContext(ThemeContext);
  const theme = isLightTheme ? light : dark;
  return (
    <div
      className="movie-list"
      style={{ color: theme.syntax, background: theme.bg }}
    >
      {" "}
      <ul>
        {" "}
        <li style={{ background: theme.ui }}>The Shawshank Redemption</li>{" "}
        <li style={{ background: theme.ui }}>Avengers End Game</li>{" "}
        <li style={{ background: theme.ui }}>The God Father</li>{" "}
      </ul>{" "}
    </div>
  );
};
export default MovieList;

You can see that we have imported the ThemeContext instance object from the ThemeContext.js file.

Now, by using the useContext hook provided by React we can access the state values in the ThemeContext. As you remember, the state values were injected in the value prop in ThemeContext Provider. So, we have accessed it here. By using the values from the context, we are conditionally rendering out the theme styles.

Bear in mind, we have not consumed the context. That's why we will not be able to access the values in the ThemeContext unless we take specific steps.

Consuming the Context Object

To consume the context object, we need to go to the App.js file and import the ThemeContextProvider function as well as the MovieList component. file. Then, we need to define the MovieList component inside the main render function wrapped with ThemeContextProvider.

Note that now every component wrapped inside the ThemeContextProvider can consume the context object and use its state variables.

Check the code:

import React from "react";
import "./App.css";
import MovieList from "./BookList";
import ThemeContextProvider from "./context/ThemeContext";
function App() {
  return (
    <div className="App">
      {" "}
      <ThemeContextProvider>
        {" "}
        <MovieList />{" "}
      </ThemeContextProvider>{" "}
    </div>
  );
}
export default App;

Now, if we run our project, we will get the following result:

As you can notice, the movie list is displayed with style properties accessed from the ThemeContext context object instance. This proves that the MovieList component has now access to the global data in the ThemeContext object. The global variables inside the ThemeContext were accessed using useContext React Hook.

Implement a Toggle Theme

Now, we are going to toggle the theme to a different style using a button.

But first, we need a function trigger inside the context in order to change the state. We are going to implement a toggleTheme function and use the setState method that helps to update the state value. Then,we need to define the function in the value prop of the ThemeContext Provider as shown in the code snippet below:

const ThemeContextProvider = (props) => {
  const [state, setState] = useState({
    isLightTheme: true,
    light: { syntax: "#555", ui: "#ddd", bg: "#eee" },
    dark: { syntax: "#ddd", ui: "#333", bg: "#555" },
  });
  const toggleTheme = () => {
    setState({ ...state, isLightTheme: !state.isLightTheme });
  };
  return (
    <ThemeContext.Provider value={{ ...state, toggleTheme: toggleTheme }}>
      {props.children}
    </ThemeContext.Provider>
  );
};

Now, we are going to implement a component called ToggleTheme.js in ./src directory which will hold the code for the button to change the theme style. Just like in MovieList.js, we import the ThemeContext and emply the useContext hook to fetch the toggleTheme function that is provided by the ThemeContext Provider. Lastly, we call the toggleTheme function in the onClick event of the button as such:

import React, { useContext } from "react";
import { ThemeContext } from "./context/ThemeContext";
const ThemeToggle = () => {
  const { toggleTheme } = useContext(ThemeContext);
  return <button onClick={toggleTheme}>Toggle the theme</button>;
};
export default ThemeToggle;

Finally, we import the ToggleTheme button component inside the App.js and render it inside of ThemeContextProvider so that the ThemeContext object can be consumed by it. The implementation is this:

import ToggleTheme from "./ToggleTheme";
function App() {
  return (
    <div className="App">
      {" "}
      <ThemeContextProvider>
        {" "}
        <MovieList /> <ToggleTheme />{" "}
      </ThemeContextProvider>{" "}
    </div>
  );
}

Now, we are able to change the theme style with a click of the button as shown in the demo below:

This theme-changing React app demonstrates the simple implementation of React Context API and hooks. The implementation does not require other dependencies to create a central store context.

Now, the global data inside the ThemeContext object can be used in all the nested components as long as it is consumed inside the ThemeContextProvider component. That's why the implementation is simple and does not require a boilerplate complex setup like Redux.

*The overall coding implementation of this project is provided in Codesandbox.

When to use Redux instead of Context API

Though React Context API and hooks are the more simple and lightweight solution to Redux, Redux is far from dead. Even if it requires the boilerplate setup with numerous library dependencies, it still remains the top solution when we aim for state management and prop drilling.

One of the major issues related to the overuse of Context API is that it triggers a re-render on each update of the state. It triggers not just the re-render of the consumer component, but also all the context-related components.

Devising a solution for this can be a hassle if done manually. So for complex state management React projects with high-frequency updates, React Context API is definitely not a good solution. This is where Redux is very useful.

In this case, it is highly recommended to use React Context API and hooks for slightly lower-end, more simple React projects or with functions where the updating frequency is low (such as changing the language, themes, user authentication, etc.). Moreover, when it comes to React projects with complex state phenomena, Redux is the one and only best solution.

The point is, it is essential to plan for the complexity of the React app and its state management mechanism. Only by defining the type of project beforehand can one know which is the best option.

Let's Recap!

The main aim of this article was to demonstrate the usability of the Context API and hooks over the Redux state management tool.

Learning what React Context API is and what advantages it has over the Redux mechanism is essential to build a standard React app. For our purpose, it is vital to be aware that React Context API is not a full-fledged powerful solution over Redux. Redux is still the top-notch option when it comes to managing state in highly complex React apps.

The bottom line is: Context API and hooks are the lightweight alternatives to Redux (which requires its own boilerplate) and it is essential to understand the nature of your React project in order to decide which one suits them best.

We suggest using Context API where the component re-render frequency is low, which is of course in simple React projects. Redux is for complex React projects where the tendency of components re-rendering is high.

As early adopters of React, we've become experts in it over the years. We like to share this knowledge, here on the blog, or by collaborating on exciting projects. Drop us a message at [email protected] if you need a trusted React partner or just subscribe below to our newsletter if you want this kind of articles in your inbox.

Have an app idea? It’s in good hands with us.

Contact us
Contact us