Context in React, what it is and how to use it.

Context in React, what it is and how to use it.

In React, if we want to pass props to a nested child component, we usually have to thread it through the child's ancestors like so:

function App() {
    const data = "Hello world"
    return <FirstAncestor data={data} />
}

function FirstAncestor(props) {
    return <SecondAncestor data={props.data} />
}

function SecondAncestor(props) {
    return <TargetComponent data={props.data} />
}

function TargetComponent(props) {
    return <h1>{props.data}</h1>
}

This can be cumbersome if we have to pass props down to a deeply nested component in a large component tree.

One solution to this is by creating a Context in React usually located in a separate module, and consume it at any child component's level without threading it through its ancestors.

In this article, you'll learn:

  • What Context is in React

  • How to create it and,

  • How to consume it in any component at any level

What Context is

According to the documentation...

Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Context - React Docs

Some benefits of using context include

  • Theming

  • Accessing the current user

Creating a context

We can create a context with React.createContext(defaultValue) and export it for reuse throughout our app, like so:

// in UserContext.js
import React from 'react';

const UserContext = React.createContext("Jeffrey");

export { UserContext }

This context object can then be used in any component at any level by consuming it.

Consuming or using a context

Now to consume a context in a component, we need to wrap it in the context's Consumer.

The Consumer is a component derived from the context that takes a function as a child and passes the context object to be used by the component. Here's what it looks like:

import { UserContext } from './UserContext';

function App() {
    return <First />
}

function Name() {
    return (
        <UserContext.Consumer>
            {(contextValue) => {
                return (
                    <p>{contextValue}</p>
                )
            }}
        </UserContext.Consumer>
    );
}

function FirstAncestor() {
    return <SecondAncestor />
}

function SecondAncestor() {
    return <Name />
}

As you can see above, we're not passing any props through the ancestors down to <Name /> in order to use the value of UserContext. We can just use it directly and this simplifies our component tree greatly

For a class component, we can use a Context by assigning it's contextType property to the context and then retrieve it in the render function by calling this.context. Like so:

import { UserContext } from './UserContext';
class Name extends React.Component {
    render() {
        let userName = this.context;
        return (
            <p>{userName}</p>
        );
    }
}
Name.contextType = UserContext;
export default Name;

Setting context values

Besides using the default values we set when creating the context object, we can change the value of the context used by a component by wrapping it in the context's Provider and specify a new value by changing it's value prop.

This way, a component can use a different context data from other components, by consuming the value of the nearest context Provider in the component tree. Here's what it looks like:

import { UserContext } from './UserContext';

function App() {
    return (
        <>
            <UserContext.Provider value="Martins">
                <Name />
            </UserContext.Provider>
            <Name />
        </>
    )
}

function Name() {
    return (
        <UserContext.Consumer>
            {(contextValue) => {
                return (
                    <p>{contextValue}</p>
                )
            }}
        </UserContext.Consumer>
    );
}

This renders "Martins" first to the screen and then "Jeffrey". Notice that the original context did not change.

The reason this happens is because the first <Name /> component wrapped by <UserContext.Provider> consumes the value of that provider while the second one simply uses the default value of UserContext.

This way, we can reuse the same component and render something entirely different without much changes to its structure

Accessing context values with useContext hook

To access context values in your component besides rendering it, we need to use the useContext hook, passing it our context object and retrieving the context values. Like so

import { UserContext } from './UserContext';
import { useContext } from 'react';

function App() {
    const userName = useContext(UserContext);

    console.log("The user's name is: ", userName)
    return (
        <p>Check the console for the user's name</p>
    )
}

With this, we can do anything we want with the context object

useContext returns a copy of the context values, not a reference to the main context since it's immutable

Conclusion

In this article, I believe you've understood what Context is in React, and how to use it to simplify your component tree.

Thanks for reading, and I hope this article helps you. If you have any questions, you can drop a comment below and I'll reply.

Also, you can connect with me online via any of these platforms

Have a nice day.