How To Use The React Context API
Tags: javascript, reactcontextapi, frontend, react
Published: 2018-10-10 17:10:52

---
title: How To Use The React Context API
published: true
tags: javascript,react-context-api,frontend,reactjs
canonical_url: https://medium.com/flatiron-labs/how-to-use-the-react-context-api-70a76d3974d5
---

## Reacting to Context

![](https://cdn-images-1.medium.com/max/1600/1*fxn1R_dXIB0yF88pG8M6bg.jpeg)

If you’ve recently been hearing a lot about the new [**React Context API**](https://reactjs.org/docs/context.html) and wanted to know more about it, this post is for you. This article will show some examples of how to use React context for managing shareable state and actions while avoiding the dreadful issues related to [**Prop Drilling**](https://dev.to/kentcdodds/prop-drilling-38-temp-slug-4820416) in a React app.

![](https://cdn-images-1.medium.com/max/1024/1*KqjVs_Y1p26VsxGqhncD3g.png)<figcaption>source: <a href="https://www.dictionary.com/browse/context">https://www.dictionary.com/browse/context</a></figcaption>

## Building an Account Profile App

In the following walkthrough, we are going to be building a simple Account Profile app. Something like this:

![](https://cdn-images-1.medium.com/max/348/0*ALMcT8hm3Ql-jMNZ)

### Initial Component and File structure

Using the above mock, you can see that we have 3 parts to the app:

1. A navigation bar with a link to a home page and the account’s username
2. A detail component that displays the account info
3. A form to create a new username or change the membership level of an account

To follow along with the code in this app, run the following commands to bootstrap the project.

```
$ npx create-react-app account-profile-app
$ cd account-profile-app
$ yarn add react-router-dom // or npm install react-router-dom
```

The first thing we need to do is lay out the foundational component structure of the app. We will then connect the components to an **AccountProvider** component created with the **Context API.**

Setup and create the necessary files to make the following file structure:

```
| src 
 | NavigationBar.jsx
 | AccountProfile 
 index.jsx 
 AccountDetails.jsx 
 AccountUpdate.jsx 
 App.jsx 
 index.js
```

For the navigation bar, we need to create a stateless component called **NavigationBar**. This will display links to the home page and the account profile page.

```jsx
// NavigationBar.jsx

import React from 'react'
import { Link } from 'react-router-dom'

const NavigationBar = ({ username }) => (
  <div>
    <Link to="/">Home</Link>
    <Link to="/account/profile">{username}</Link>
  </div>
)

export default NavigationBar
```

Now, let’s build the **AccountDetails** component that will display the account’s username, date joined, and membership level.

```jsx
// AccountProfile/AccountDetails.jsx

import React from 'react'

const AccountDetails = ({ username, dateJoined, membershipLevel }) => (
  <div>
    <p>Username: {username}</p>
    <p>Date Joined: {dateJoined}</p>
    <p>Membership Level: {membershipLevel}</p>
  </div>
)

export default AccountDetails
```

Next, we can create the **AccountUpdate** component that will be a basic form allowing the creation of a new username or change a membership level. This component will also need a small amount of internal state to handle on change and form submission events.

```jsx
// AccountProfile/AccountUpdate.jsx

import React from 'react'

class AccountUpdate extends Component {
  state = {
    username: this.props.username,
    membershipLevel: this.props.membershipLevel
  }

  handleOnChange = ({ target: { value, name } }) => {
    this.setState({
      [name]: value
    })
  }

  render () {
    const { membershipLevel, username } = this.state

    return (
      <div>
        <form>
           <label htmlFor="username">New Username</label>
           <div>
             <input 
               type="text" 
               name="username" 
               value={usernameValue} 
               onChange={this.handleOnChange} 
             />
           </div>
           <label htmlFor="membershipLevel">Membership Level</label>
           <div>
             <select 
               value={membershipLevel} 
               name="membershipLevel"
               onChange={this.handleOnChange}
             >
               <option value="Bronze">Bronze</option>
               <option value="Silver">Silver</option>
               <option value="Gold">Gold</option>
             </select>
           </div>
           <button>Save</button>
         </form>
      </div>
    )
  }
}

export default AccountUpdate
```

Now that the the initial **AccountUpdate** and **AccountInfo** components are built, we should place them inside of an **AccountProfile** component.

```jsx
// AccountProfile/index.jsx  

import AccountDetails from './AccountDetails'
import AccountUpdate from './AccountUpdate'

const AccountProfile = ({ account: { username, dateJoined, membershipLevel } }) => (
  <Fragment>
    <AccountDetails username={username} dateJoined={dateJoined} membershipLevel={membershipLevel} />
    <AccountUpdate username={username} membershipLevel={membershipLevel} />
  </Fragment>
)

export default AccountProfile
```

Finally, we can mount the components we created to the **App** component wrapped inside a **Router** component. We also need to create an **account** object that will be our temporary state holder for account info until we add in the **AccountProvider**.

```jsx
// App.jsx

import React from 'react'
import { BrowserRouter as Router, Link } from 'react-router-dom'
import NavigationBar from './NavigationBar'
import AccountProfile from './AccountProfile'

const account = {
  username: 'Crunchy Crunch',
  dateJoined: '9/1/18',
  membershipLevel: 'Silver'
}

const App = () => (
  <Router>
    <Fragment>
      <Navbar username={username} />
      <Switch>
        <Route 
          exact 
          path="/" 
          render={() => <div>Home</div>}
        />
        <Route 
          exact 
          path="/account/profile" 
          render={() => <AccountProfile account={account} />} 
        />
      </Switch>
    </Fragment>
  </Router>
)

export default App
```

![](https://cdn-images-1.medium.com/max/350/1*9smELZjfNRokxCC4QxI_Gw.gif)

## Adding Context to our App

With the initial components we created, the account data for the **NavigationBar** and **AccountProfile** was hard-coded in the **App** component and then passed down as props to the other components. The passing down of props is not out of hand yet, but this could be troublesome as our app grows.

Currently, we are passing down props about the account from the **App** component to the child **NavigationBar** component. We are also passing down props from the **App** component to the child **AccountProfile** component, which then passes props down to its children. This is where passing props is starting to become a problem in our app and a typical case of **“Props Drilling”,** as mentioned earlier. We want to make our app more flexible and not have to adjust code in so many places during a refactor to avoid brittle code. This is where a solution like **Redux** or **MobX** might have been used, but now we can use **React Context** to simplify this problem for us.

Our solution for this will be to to set up a data provider using the context API. This will allow us to share state with our components, while using a [**Singleton Design Pattern**](https://www.sitepoint.com/javascript-design-patterns-singleton/) for our data store that all of our components can listen / subscribe too.

So for the next step, let’s set up an **AccountProvider** and an **AccountConsumer** by creating a new context object and **React** component that contains local state. We will later integrate this data provider with our components to get dynamic data.

```jsx
// src/providers/AccountProvider.jsx

import React from 'react'

// Set Up The Initial Context
const AccountContext = React.createContext()

// Create an exportable consumer that can be injected into components
export const AccountConsumer = AccountContext.Consumer

// Create the provider using a traditional React.Component class  
class AccountProvider extends Component {
  state = {
    username: 'Crunchy Crunch',
    dateJoined: '9/1/18',
    membershipLevel: 'Silver'
  }

  render () {
    return (
      // value prop is where we define what values 
      // that are accessible to consumer components
      <AccountContext.Provider value={this.state}>
        {this.props.children}
      </AccountContext.Provider>
    )
  }
}

export default AccountProvider
```

Now that we’ve created the **AccountProvider & Account Consumer** , we can connect / render it on the top level of our **App** component. This will make the **AccountProvider** values accessible to all children components. We will also remove our temporary **account** object, and its use as a prop passed to the **NavigationBar** and **AccountProfile** components.

```jsx
// App.jsx — update

// ... previous imports
import AccountProvider from './providers/AccountProvider'

// remove const account

const App = () => (
  <AccountProvider>
    <Router>
      <Fragment>
        <NavigationBar />
        <Switch>
          <Route 
            exact 
            path="/" 
            render={() => <div>Home</div>}
          />
          <Route 
            exact 
            path="/account/profile" 
            component={AccountProfile} 
          />
        </Switch>
      </Fragment>
    </Router>
  </AccountProvider>
)
```

Let’s talk about what we’ve completed so far:

1. Setup our components
2. Created an **AccountProvider** using the context API
3. Created an **AccountConsumer** that will be used by child components to access values from the **AccountProvider**
4. Mounted the **AccountProvider** as the top level component in our application
5. Removed our temporary **account** object and simplified our props

Everything is in good shape so far, but how do we make our components subscribe to our **AccountProvider**? Let’s take a shot with the **NavigationBar** first.

```jsx
// NavigationBar.jsx — Update

// ... previous imports
import { AccountConsumer } from '../providers/AccountProvider'

const NavigationBar = () => (
  <AccountConsumer>
    {({ details: { username } }) => (
      <div> 
        <Link to="/">Home</Link>
        <Link to="/account/profile">{username}</Link>
      </div>
    )}
  </AccountConsumer>
)

export default NavigationBar
```

Ok, so that was a lot. Let’s do one more recap of our accomplishments:

1. We imported the **AccountConsumer**
2. We rendered the **AccountConsumer** as the top level component of the **NavigationBar**
3. We defined a callback block that passed the username value from the local state of the **AccountProvider**
4. We rendered the original code of the **NavigationBar** component that now uses the new username prop given by the **AccountConsumer**

This made the **NavigationBar** an active subscriber of the **AccountProvider**. If the username state is changed in the **AccountProvider,** it will now render that new value in the **NavigationBar.**

Next, let’s update the **AccountDetails,** using the same pattern, but passing the values username, membershipLevel, and dateJoined.

```jsx
// AccountProfile/AccountDetails.jsx — Update

// ... previous imports 
import { AccountConsumer } from '../providers/AccountProvider'

const AccountDetails = () => (
  <AccountConsumer>
    {({ username, dateJoined, membershipLevel }) => (
      <div>
        <p>Username: {username}</p>
        <p>Date Joined: {dateJoined}</p>
        <p>Membership Level: {membershipLevel}</p>
      </div>
    )}
  </AccountConsumer>
)

export default AccountDetails
```

Awesome job! We are showing data defined in the **AccountProvider** inside of our **NavigationBar & AccountDetails** components.

We just need to make one adjustment to the **AccountProfile** component to remove unused prop drilling now.

```jsx
// AccountProfile/index.jsx - Update  

import AccountDetails from './AccountDetails'
import AccountUpdate from './AccountUpdate'

const AccountProfile = () => (
  <Fragment>
    <AccountDetails />
    <AccountUpdate />
  </Fragment>
)

export default AccountProfile
```

Yay, we are at the halfway point of the application. Feel free to take a break and read up on passing props from context providers to children components (I highly recommend “[React’s New Context API](https://medium.com/dailyjs/reacts-%EF%B8%8F-new-context-api-70c9fe01596b)”). Continue on to see how to dynamically update state in our **AccountProvider**.

#### **Dynamically Updating Provider Context State**

To complete the following task, we will need to create a new function in our **AccountProvider** that can update the local state for our subscribed components to receive.

Let’s call this function `updateAccount()` and then bind it to the state object.

```jsx
// AccountProvider.jsx — update

import React from 'react'

const AccountContext = React.createContext()

export const AccountConsumer = AccountContext.Consumer

class AccountProvider extends Component {
  state = {
    username: 'Crunchy Crunch',
    dateJoined: '9/1/18',
    membershipLevel: 'Silver',
    updateAccount: updatedAccount => this.updateAccount(updatedAccount)
  }

  updateAccount = updatedAccount => {
    this.setState(prevState => ({
      ...prevState,
      ...updatedAccount
    }))
  }

  render () {
    return (
      <AccountContext.Provider value={this.state}>
        {this.props.children}
      </AccountContext.Provider>
    )
  }
}

export default AccountProvider
```

With this newly created code in the **AccountProvider,** we’ve added:

1. An updateAccount key to the local state that references a bound `updateAccount()` function
2. An `updateAccount()` function that updates the local state of the **AccountProvider**
3. Sets `updateAccount()` as a passable function to subscribers

With this new function available to us, we can now update the **AccountUpdate** component’s form to use it. Let’s place it inside of a handleOnSubmit function and pass it the update account information from our form. We should also pass down the username and membership level from the **AccountProvider**.

```jsx
// AccountProfile/AccountUpdate.jsx — update

import React from 'react'
import { AccountConsumer } from '../providers/AccountProvider'

class AccountUpdate extends Component {
  // Updates!!
  state = {
    username: this.props.username,
    membershipLevel: this.props.membershipLevel
  }

  handleOnChange = ({ target: { value, name } }) => {
    this.setState({
      [name]: value
    })
  }

  // New Code!!
  handleOnSubmit = event => {
    event.preventDefault()

    const updatedAccount = { ...this.state }

    this.props.updateAccount(updatedAccount)
  }

  // New Code!!
  // To handle resetting form upon submission success
  componentWillReceiveProps(nextProps, prevProps) {
    if(prevProps !== nextProps) {
      this.setState({
        username: nextProps.username, 
        membershipLevel: nextProps.membershipLevel
      })
    }
  }

  // Updates!!
  render () {
    const { membershipLevel, username } = this.state
    const usernameValue = username === this.props.username ? '' : username

    return (
      <div>
        <form onSubmit={this.handleSubmit}>
          <label htmlFor="username">New Username</label>
          <div>
            <input 
              type="text" 
              name="username" 
              value={usernameValue} 
              onChange={this.handleOnChange} 
            />
          </div>
          <label htmlFor="membershipLevel">Membership Level</label>
          <div>
            <select 
              value={membershipLevel} 
              name="membershipLevel"
              onChange={this.handleOnChange}
            >
              <option value="Bronze">Bronze</option>
              <option value="Silver">Silver</option>
              <option value="Gold">Gold</option>
            </select>
          </div>
          <button>Save</button>
        </form>
      </div>
    )
  }
}

// Added to connect AccountConsumer
// To pass props to AccountUpdate
// Before component initialization
// As the AccountUpdate.state requires
// The new props
const ConnectedAccountUpdate = props => (
  <AccountConsumer>
    {({ username, membershipLevel, updateAccount }) => (
      <AccountUpdate  
        {...props}
        username={username}
        membershipLevel={membershipLevel}
        updateAccount={updateAccount}
      />
    )}
  </AccountConsumer>  
)

export default ConnectedAccountUpdate
```

Voilà! On form submission, we are now updating the username and membership level dynamically, which, now appears with the new changes in the **NavigationBar** and **AccountDetails** components.

## Conclusion:

I hope this article provides you with some ideas on how you can use it in your current projects. I’ve enjoyed my time working with the new tool, and appreciate having even more control of my React code.

Want to work on a mission-driven team that loves sane prop management in its React apps? [**We’re hiring**](https://flatironschool.com/careers/) **!**

![Footer top](https://cdn-images-1.medium.com/proxy/1*cN2uavSo9qa-SVEi6GAp2A.png)

**To learn more about Flatiron School, visit the** [**website**](http://www.flatironschool.com/) **, follow us on** [**Facebook**](http://www.facebook.com/flatironschool) **and** [**Twitter**](http://www.twitter.com/flatironschool) **, and visit us at** [**upcoming events**](https://flatironschool.com/events/) **near you.**

**Flatiron School is a proud member of the** [**WeWork**](https://www.wework.com/) **family. Check out our sister technology blogs** [**WeWork Technology**](https://engineering.wework.com/) **and** [**Making Meetup**](https://medium.com/making-meetup) **.**

![Footer bottom](https://cdn-images-1.medium.com/proxy/1*vDaS9kLQz7Rf42nHubFrgw.png)

* * *