In a typical React application, we pass data from parent to child components. In other words, we follow a top-down approach. However, such an approach can become problematic for certain data categories such as theme settings, locale info etc. React Context makes it easy to handle passing such data between components. In this post, we will learn how to use React Context API with Class Component.

React Context API provides first class support for class components. To use React Context, we use the Provider component to provide the shared data. In order to subscribe to the Context in class components, we use the contextType property of a class. However, there are some important aspects to consider while structuring your components for using React Context.

1 – When to use React Context API?

React Context API facilitates sharing of global data between various components. Examples of global data are:

  • authenticated user data
  • application theme
  • locale information such as preferred language or timezone

Without using React Context, we need to pass the information through the component tree using Props. This approach is also known as prop-drilling. If the component tree is made of several components, prop drilling can become a huge problem and cause issues. This is where React Context becomes a better approach as compared to props.

2 – React Context Class Component Example

Let us build a small example application to demonstrate using React Context API with Class Components

We will first initialize the context using React.createContext() function. Since the context is a common piece of data, we will place it within its own file.

import React from 'react';

export const ContextThemeColor = React.createContext('crimson')

While calling the createContext() function, we can also provide a default value. Basically, this is the default value for the React Context. However, we can change the default value when we declare a Provider.

Next, we can create the Provider as below:

import React from 'react';
import { ButtonHolder } from './ButtonHolder';
import { ContextThemeColor } from './ContextThemeColor';


export class ContextDemo extends React.Component {

    render() {
        return (
            <ContextThemeColor.Provider value="cyan">           
               <ButtonHolder />
            </ContextThemeColor.Provider>         
        )
    }
}

Basically, ContextDemo is a React class component. In this component, we import the Context and use it to wrap other components where we want the context to be available.

The Provider accepts a value prop as input. It passes the value to consumer components that are also descendants of the Provider. One Provider can be connected to several consumers of the context.

React Context triggers a render of all descendants of the Provider whenever there is a change in the value prop. A change is determined by using the standard Javascript Object.is method.

In our example, the ContextThemeColor.Provider wraps the ButtonHolder component.

import React from "react";
import { ContextButton } from "./ContextButton";

export class ButtonHolder extends React.Component {
    render() {
        return (
            <React.Fragment>
                <ContextButton />
            </React.Fragment>
        )
    }
}

As you can see, ButtonHolder is a simple class component. It has another child component known as ContextButton where we will consume the React context.

import React from 'react';
import { ContextThemeColor } from './ContextThemeColor';

export class ContextButton extends React.Component {
    render() {
        let color = this.context;
        return <button style={{ backgroundColor: color }}>Context Button</button>
    }
}

ContextButton.contextType = ContextThemeColor;

In a React Context Consumer, we assign the Context object to the contextType property of the class component. In this case, the class is ContextButton. Then, we can simply extract the value of the context from this.context and use it in the component.

We can reference this.context is any React component lifecycle method including render.

3 – React Dynamic Context Provider for Class Component Example

While the example in the previous section showed the power of React Context, it was not a very useful application. Most of the times, we need to have dynamic values in our context.

React Dynamic Context Provider helps us make the context object dynamic for the class components. We can also change the context value based on user action.

To demonstrate the same, let us tweak our example. Firstly, we will create a new Context object.

import React from 'react';

export const buttonThemes = {
    primary: {
        color: "white",
        background: "#45c496"
    },
    secondary: {
        color: "whitesmoke",
        background: "#d91b42"
    }
}

export const ButtonThemeContext = React.createContext(
    buttonThemes.primary
)

As you can see, we have a couple of themes for our button with different background color and font color. Also, we use React.createContext() to create the default context with the primary theme.

Let us know modify our React Context Provider class component.

import React from 'react';
import { ButtonHolder } from './ButtonHolder';
import { ButtonThemeContext, buttonThemes } from './ButtonTheme';

export class ContextDemo extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            buttonTheme: buttonThemes.primary
        }

        this.changeButtonTheme = () => {
            this.setState(state => ({
                buttonTheme: state.buttonTheme === buttonThemes.primary
                ? buttonThemes.secondary
                : buttonThemes.primary,
            }))
        };
    }

    render() {
        return (
            <div>
                <ButtonThemeContext.Provider value={this.state.buttonTheme}>
                    <ButtonHolder />
                </ButtonThemeContext.Provider>
                <button onClick={this.changeButtonTheme}>Change Theme</button>
            </div>
            
        )
    }
}

This is a far more interesting Provider component.

Basically, we are giving the user an option to toggle between the primary and secondary themes. To do so, we create another button known as Change Theme. On clicking this button, we invoke the changeButtonTheme() function that changes the button theme by updating the state variable.

In the ButtonThemeContext.Provider, we are not using a hard-coded value. Instead, we are tying the value prop to the state variable buttonTheme. In other words, every time the user changes the theme, the React Context Provider’s value prop changes. This triggers a re-render of the child component within the Provider’s hierarchy.

There is no change in the ButtonHolder component. However, we modify the ContextButton class component as below.

import React from 'react';
import { ButtonThemeContext } from './ButtonTheme';

export class ContextButton extends React.Component {
    render() {
        let theme = this.context;
        return <button style={{ backgroundColor: theme.background, color: theme.color }}>Context Button</button>
    }
}

ContextButton.contextType = ButtonThemeContext;

In the ContextButton class component, we assign the value of the Context (in this case, ButtonThemeContext) to the contextType. Then, we can extract the current theme object from the context and use it to dynamically style the button component.

When the user toggles the theme, the React Context update triggers a re-render with the new theme values.

4 – Important Questions about React Context

While React Context is easy enough to understand, there are also several important questions about the topic in general. Let us look at a couple of such questions.

4.1 – React Context vs State – which is better?

One of the most common points is the difference between React Context and component state.

Both React Context and component state are used to store data for class components as well as functional components. However, state is for local component-level data. This data cannot be easily shared with several components. As result, component state is not a great place to store common data such as application theme, logged in user and so on. The React Context API provides an ideal way to store and make the common data available in the entire component tree without unnecessary prop drilling.

4.2 – Can we pass React Context from Child to Parent?

The use of React component is to pass data from the parent to child component. Therefore, we cannot use React Context API to transfer data from child to parent. Instead, we should utilize custom props and callback function to achieve the same.

Conclusion

React Context API works seamlessly with class components. We looked at a basic example of React Context and then, a more complex example using a dynamic context provider.

However, functional components have become the de-facto approach of building React applications. React Context also works with functional components and hooks. Check out our detailed post on React Context with Functional Component.

If you have any comments or queries about this post, please feel free to mention in the comments section below.

Categories: React

0 Comments

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *