context-api-react

3 Min. Read
Sep 22, 2019

Context in React

While engineering a React application, one issue that concerns all developer is managing the state of the application. Traditional method of passing down state as props is not ideal as it requires state to be saved at the closes ancestor of all the components that might require the contents of the state. While most would recommend using redux that provides a container for state and methods to access the same, learning and using redux can be a bit off putting for beginners as it comes with a lot of boilerplate and methods associated with it, one can almost make do without using redux by the traditional method of passing down state as props, or by using the context api that allows us to achieve the same result of passing state down to the components that need it.

Although the tradition method of passing down state as props will suffice for small application with not many levels of component nesting, the problem arises when our application grows in size and components are nested to multiple levels. When a sibling component might require state of a component, we ought to lift the state up to the nearest ancestor component common to both the component that requires the state and the component that has the state. Also, while passing down the state as props to child components, intermediate components that might not actually have anything to do with the state have to process the state passed down from the parent component and further pass it down to the child component as props.

Redux solves this by creating a data store that is separate from your application tree and through which we can inject the data any level that we want. React’s context is often a simpler alternative to Redux.

Creating Context

There are two aspects of a context; a consumer and a provider. First we need to create a new context.

1
  const MyContext = React.createContext();

Provider component is where the data is actually going to live. We need to return a context provider in the Provider Component. This lives at the top level of the application and can be accessible using another consumer component wherever necessary.

1
2
3
4
5
6
7
8
9
10
11
12
  class MyProvider extends Component {
    state = {
    //your state
        }
    render() {
        return (
            <MyContext.Provider value= {{state: this.state}}>
                {this.props.children}
            </MyContext.Provider>
            )
        }
    }

Now wrap the components with a provider to access the value made available by it.

1
2
3
4
5
6
7
8
9
10
11
class App extends Component {
    render() {
        return (
            <MyProvider>
                <div>
                    <ChildComponent />
                </div>
            </MyProvider>
         )
    }
}
1
2
3
4
5
const ChildComponent = (props) => {
    <div>
        <GrandChildComponent />
    </div>
}

We can access the data provided by the context using the Consumer Component provided by context.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class GrandChildComponent extends Component {
    render() {
        return (
         <div>
           <MyContext.Consumer>
              {(context) => (
                <React.Fragment>
                    <p>{context.state}</p>     //outputs the state from MyProvider Component
                </React.Fragment>
              )}
           </MyContext.Consumer>
          </div>
         )
    }
}

To update the state

To update the state, we can pass down functions or actions exactly like how we’ve passed down state to the component that needs to update the state.

In the Provider, define a function

1
2
3
4
5
6
7
8
  <MyContext.Provider value= {
        {
        state: this.state
        someMethod: () => this.setState ({
              value: this.state.value + 1
            })
        }
}>

Access the functions from inside the consumer

1
2
3
4
5
6
7
8
  <MyContext.Consumer>
     {(context) => (
       <React.Fragment>
           <p>{context.state}</p>     //outputs the state from MyProvider Component
           <button onClick = {context.someMethod}
       </React.Fragment>
     )}
  </MyContext.Consumer>