Sharing State with the React Context API
Tags: react, programming, webdev, javascript
Published: 2021-01-20 23:11:28

**Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62**

**Subscribe to my email list now at http://jauyeung.net/subscribe/**

React is a library for creating front end views. It has a big ecosystem of libraries that work with it. Also, we can use it to enhance existing apps.

React components can only pass data from parent to child via props. The Context API adds to that by allowing components with other relationships to share data.

In this article, we’ll look at how to use it to share data between components.

When to Use Context
===================

We should use Context to share data between React components. However, it should be used sparingly since it creates tight coupling between components.

To use it within a simple app, we can write the following:

```
const ColorContext = React.createContext("green");

class Button extends React.Component {  
  render() {  
    return (  
      <div>  
        <ColorContext.Consumer>  
          {value => (  
            <button style={{ color: value }}>{this.props.children}</button>  
          )}  
        </ColorContext.Consumer>  
      </div>  
    );  
  }  
}

class App extends React.Component {  
  render() {  
    return (  
      <div>  
        <ColorContext.Provider value="blue">  
          <Button>Click Me</Button>  
        </ColorContext.Provider>  
      </div>  
    );  
  }  
}
```

In the code above, we created a Context to share data by writing:

```
const ColorContext = React.createContext("green");
```

`createContext` takes a default value as an argument, where we passed in `'green'` .

Then in the `App` component, we have the `ColorContext.Provider` component with the `value` prop set to the value that we want to share.

In the example above, it’ll be `'blue'` . We wrapped it around the components that we want to share the data with so that we can access the value from that component.

In this case, we created a new `Button` component, which has the `ColorContext.Consumer` component. Inside it, we can get the value shared from the context provider from the `value` parameter in the function we inserted inside the `ColorContext.Consumer` component.

`value` should be set to `'blue'` since that’s what we set as the value of the `value` prop.

Inside the function we passed in the consumer, we returned a `buttom` element with the style prop and we set the `color` style to `value` , which is `'blue'` .

Alternatives to Context
=======================

If we want to pass data into a deeply nested component, we can instead pass in the whole component down to where we want it. This way, we don’t have to worry about passing props to multiple levels to pass something that’s only needed by deeply nested components.

For example, if we want to pass the color prop to `Button` components, which is contained in a `ButtonBar` . We can do that as follows:

```
class Button extends React.Component {  
  render() {  
    return (  
      <button style={{ color: this.props.color }}>{this.props.children}</button>  
    );  
  }  
}

class ButtonBar extends React.Component {  
  render() {  
    return this.props.buttons;  
  }  
}

class App extends React.Component {  
  render() {  
    const buttons = [  
      <Button color="blue">Click Me</Button>,  
      <Button color="green">Click Me 2</Button>  
    ];  
    return <ButtonBar buttons={buttons} />;  
  }  
}
```

In the `App` component, we have the `Button` components in the `buttons` array. Then we passed the whole array straight down to the `ButtonBar` component.

Then `ButtonBar` just returns what we passed in, which is `this.props.buttons` .

This also means more complexity in the higher-order components, so it may not be suitable in all cases.

Updating Context from a Nested Component
========================================

We can pass in functions to the object that we pass into `createContext` so that we can call them inside the component that has the context consumer component.

For example, we can write the following:

```
const colorObj = {  
  color: "green",  
  toggleColor: () => {}  
};

const ColorContext = React.createContext(colorObj);  
class Button extends React.Component {  
  render() {  
    return (  
      <div>  
        <ColorContext.Consumer>  
          {({ color, toggleColor }) => (  
            <button onClick={toggleColor} style={{ color }}>  
              {this.props.children}  
            </button>  
          )}  
        </ColorContext.Consumer>  
      </div>  
    );  
  }  
}

class App extends React.Component {  
  constructor(props) {  
    super(props);  
    this.state = {  
      color: "blue",  
      toggleColor: () => {  
        this.setState(state => ({  
          color: state.color === "green" ? "blue" : "green"  
        }));  
      }  
    };  
  } 

  render() {  
    return (  
      <div>  
        <ColorContext.Provider value={this.state}>  
          <Button>Click Me</Button>  
        </ColorContext.Provider>  
      </div>  
    );  
  }  
}
```

The code above starts with defining the `colorObj` object, which is passed into `createContext` as the default value of `ColorContext` .

Then in the `App` component, we initialize `this.state` by setting it to an object with the `toggleColor` function, and the `color` property set to `'blue'` .

We pass `this.state` as the value of the `value` prop of `ColorContext.Provider` .

Then we access the whole object inside the `ColorContext.Consumer` component in the `Button` component.

Inside there, we get the `color` and `toggleColor` property from the `this.state` which we passed in from the `ColorContext.Provider` . Then we pass `toggleColor` into the `onClick` prop, and `color` into the object that we passed into the `style` prop.

Then when we click the Click Me button, the text color will toggle between blue and green.

Consuming Multiple Contexts
===========================

We can consume multiple contexts by nesting them. For example, we can do that as follows:

```
const ColorContext = React.createContext("green");  
const BorderContext = React.createContext("");
class Button extends React.Component {  
  render() {  
    return (  
      <div>  
        <ColorContext.Consumer>  
          {color => (  
            <BorderContext.Consumer>  
              {border => (  
                <button style={{ color, border }}>{this.props.children}</button>  
              )}  
            </BorderContext.Consumer>  
          )}  
        </ColorContext.Consumer>  
      </div>  
    );  
  }  
}

class App extends React.Component {  
  render() {  
    return (  
      <div>  
        <ColorContext.Provider value="blue">  
          <BorderContext.Provider value="3px solid green">  
            <Button>Click Me</Button>  
          </BorderContext.Provider>  
        </ColorContext.Provider>  
      </div>  
    );  
  }  
}
```

In the code above, we create 2 Contexts, `ColorContext` and `BorderContext` and passed in values to the `value` prop to both. We nested the providers in the `App` component, which means that both contexts can be consumed by the `Button` component inside.

Then in the `Button` component, we have consumers for both contexts nested in each other. And then we can get both values that were passed in from the providers.

We then use both values to set the styles of the `button` .

In the end, we a button with blue text and a thick green border.

Conclusion
==========

We can use the React Context API to share data between components.

It works by creating a Context object with `React.createContext` . Then we wrap the context provider component outside the components that we want to consume the context from.

Then in the component that we put inside the provider, we have the context consumer component wrapped outside of whatever we want to apply the context value to.

Finally, we can get the value inside the function that we pass inside the Context consumer.