Using Context in React

Context in React allows you to keep your data compartmentalized in stores.

Along with React 16.8 came a plethora of new features including the ability to subscribe to a central data repository or context. There are many tutorials to follow to learn how to use context, but this one sets out to make it as simple as possible. A repository exists that shows these topics in a working app.

Many of the features available now in React seem similar to features that exist in frameworks like Redux which are frequently built into React applications.

Unfortunately using a data store means that you’ll increase your code complexity slightly but it can have advantages when your application is large enough to require a central data repository for certain values.

High Level Overview

In order to use a data store we’ll need these three things.

  1. Context – The source where the data and its setters reside.
  2. Provider – The class that provides access to the context.
  3. Consumer – This is the class that will get access to the context.

These are the only three things that we need in order to work with contexts.

Create a Context

import React from 'react';

// First we need a new Context
export const CounterContext = React.createContext();

As you can see we can get a new context from the React import. I’m exporting CounterContext because I will be using it later in our counter example.

You may want to put your contexts in a central place in your application. In the demonstration repository I put my context files in src/store.

Create a Provider

// Next we need a provider 
export class CounterProvider extends Component { 
  state = { 
    counter: 0 
  }; 
  render() { 
    return (
      <CounterContext.Provider value={{
        state: this.state,
        addCount: () => this.setState({ counter: this.state.counter + 1 }),        
        subCount: () => this.setState({ counter: this.state.counter - 1 }) 
       }}> 
        {this.props.children} 
      </CounterContext.Provider> 
    ); 
  } 
}

Both the context and the provider should be in the same file above but I have it separated here for clarity. Refer to the repository if you have any questions.

Our provider needs a context to provide to a consumer. The provider requires an attribute named value that will be made available to any consumers of the context.

Fortunately value can be anything and in the code above I have included setters that allow us to easily update our state . There are many ways to approach building applications and certain people may want to use a Store for more complicated projects but that is beyond the scope of this article.

Consume the Values

Next, let’s create our consumer. This is the Counter component created in src/components/Counter.js.

import React from 'react';
import {CounterContext} from '../store/simpleContext';

function Counter(){
  return(
    <div>
      <CounterContext.Consumer>
        {(context)=>(
          <div>
            <p>Count: {context.state.counter}</p>
            <button onClick={context.addCount}>+</button>
            <button onClick={context.subCount}>-</button>
          </div>
        )
        }
      </CounterContext.Consumer>
    </div>
  )
}

export default Counter;

As you can see getting information out of a context is a little more work than getting it from props. First we have to create a consumer from the context we just made ( using CounterContext.Consumer. Then we need to pass the context in as a function parameter to component that will be built.

This is our consumer, but we still need to wrap this component in a provider.

In App.js we use our counter that we made. It is here that we need to wrap Counter in the context provider.

import React from 'react';
import {CounterProvider} from './store/simpleContext';
import Counter from './components/Counter';

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

export default App;

At this point you should have everything you need to get context working in React. Unfortunately using context adds more complexity to your project so it’s a design decision that must be made carefully. Having said that, it also allows for a more intuitive flow and compartmentalization of data. If you find that you’re having to pass props around in a deeply nested component tree you may want to think about using context to help you out.

Leave a Reply