DEV Community

Cover image for # REACT CONTEXT API
Aakash Srivastav
Aakash Srivastav

Posted on

# REACT CONTEXT API

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

In a typical React application, data is passed top-down (parent to child) via props, but this can be cumbersome for certain types of props (e.g. locale preference, UI theme) that are required by many components within an application. Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.

Let's see an example :

import React, { Component } from 'react';


const Family = (props) => (
  <div className="family">
    <Person />
  </div>
)

class Person extends Component {
  render() {
    return (
      <div className="person">
        <p>Hey I'm a {this.props.name}
      </div>
    )
  }
}

class App extends Component {
  state = {
    name: 'Aakash',
    age: 50,
    loveReact: true
  } 
  render() {
    return (
        <div>
          <p>I am the app</p>
          <Family name={this.state.name}/>
        </div>
    );
  }
}
export default App;
  • So , we've a issue here and that is we can't directly pass data from App down to Person.So , what I need to do is I need to pass the data to Family component and then my Family component would need to pass data down again to Person component.
  • So , it's fine if we've to pass only upto 2-3 levels but what if we've 6 or 7 levels , well , that's where people start to reach for some sort of data store like Redux.
  • Now React's Context api is going to work very similar to that where we can inject our data at any level we want.
  • It need two different pieces -

1) Provider
2) Consumer

  • Let's see how it works:

STEP 1 : We will make a new context

const MyContext = React.createContext();

STEP 2 : Create a Provider Component and that's where you data is actually going to live.
So , we'll move our state into our Provider Component.

class MyProvider extends Component {
  state = {
    name: 'Aakash',
    age: 50,
    loveReact: true
  }
  render() {
    return ()
    }
}
  • Now , what we actually return from our Provider component is context provider.
class MyProvider extends Component {
  state = {
    name: 'Aakash',
    age: 50,
    loveReact: true
  }
  render() {
    return (
      <MyContext.Provider value="I'm the value"   // **value** is the data we want to pass down
        this.props.children
      </MyContext.Provider>
    )
    }
}
  • So , what this is going to do is , it's going to live at the top level of application and there's is going to be another components that we'll use to actually access the data.
  • We need to pass a value to our Provider and that's going to be the actual data we want to pass down. Let's say it's "I'm the value" for now.

  • So , so far , we aren't able to access state but that's okay , we have passed down the value("I'm the value") to anything that is wrapped inside our Provider.

  • Now , we will go to our App component and wrap the entire app in that Provider.Now , any child inside the Provider , it doesn't matter if it's directly there or anywhere down the chain , we're able to access the actual data.

.
.
const Family = (props) => (
  <div className="family">
    <Person />               //  removed the props getting pass down Person component as we don't need it anymore.
  </div>
)
class App extends Component {
  render() {
    return (
      <MyProvider>
        <div>
          <p>I am the app</p>
          <Family />              //  removed the props getting pass down Family componet as we don't need it anymore.
        </div>
      </MyProvider>
    );
  }
}
  • Now , the question is How do I access my data in my Person component?
    Currently our tree is like :

  • App

    • Provider // Data lives here
    • Context.Provider
      • Family
      • Person // But we need to access it here i,e inside Person
  • The way we do that is by creating a Consumer. Provider is where your data lives and Consumer is where you actually want to access your data.So , all we need to do is to go into Person component because that's where we want to access our data.And then creating , MyContext.Consumer because , it's where we going to grab our data from.

  • And then inside of MyContext.Consumer , it's not going to pass it down via props , it's going to use render prop(i,e we will have render prop and pass it's value a function or you can pass render prop as a child also , which we're going to use here ).

  • The child of consumer is always always always be a function

.
.
class Person extends Component {
  render() {
    return (
      <div className="person">
        <MyContext.Consumer>
          {(context) => (
            <p>I am inside the {context}</p>
          )}
        </MyContext.Consumer>
      </div>
    )
  }
}

Output: I am inside the I'm the value.

  • So , we can say value is going to be the actual data we want to pass down and that lives inside of our Provider.
  • Then , when we want to actually access that data then ,we can access it inside Consumer by wrapping it in a Consumer tag and then giving the only child of that Consumer tag to be a render function and then inside , we can render our anything.

  • Now , the questin is how do I pass down state ?

  • We can pass an object containing the state object like shown below.

  • And then finally , we can access any state property that lives inside our Provider inside our Consumer.

.
.
class MyProvider extends Component {
  state = {
    name: 'Aakash',
    age: 50,
    loveReact: true
  }
  render() {
    return (
      <MyContext.Provider value={{
        state: this.state           // Passing state down.
        }
      }
    )
  }
}

class Person extends Component {
  render() {
    return (
      <div className="person">
        <MyContext.Consumer>
          {(context) => (
            <React.Fragment>
              <p>Age: {context.state.age}</p>       
              <p>Name: {context.state.name}</p> 
            </React.Fragment>
          )}
        </MyContext.Consumer>
      </div>
    )
  }
}
  • Now the question is "How do I update that state" ?

So what you can do is much like passing down states , you can also pass down your functions(actions in Redux).

  • Let's say we want to increment age by clicling on a button.
  • We'll create a function first :
.
.
  render() {
    return (
      <MyContext.Provider value={{
        state: this.state,
        growAYearOlder: () => this.setState({   // Function to increment age
          age: this.state.age + 1
        })
      }}>
        {this.props.children}
      </MyContext.Provider>
    )
  }
}

class Person extends Component {
  render() {
    return (
      <div className="person">
        <MyContext.Consumer>
          {(context) => (
            <React.Fragment>
              <button onClick={context.growAYearOlder}>Increment Score</button>     
            </React.Fragment>
          )}
        </MyContext.Consumer>
      </div>
    )
  }
}
  • BOOM BOOM! .So , we can see , we still have our actual data i,e State inside Provider but we can magically access it at any level deep by just wrapping our code in Consumer tag.

So, that it.
Here's full code :

import React, { Component } from 'react';

// first we will make a new context
const MyContext = React.createContext();

// Then create a provider Component
class MyProvider extends Component {
  state = {
    name: 'Aakash',
    age: 50,
    loveReact: true
  }
  render() {
    return (
      <MyContext.Provider value={{
        state: this.state,
        growAYearOlder: () => this.setState({
          age: this.state.age + 1
        })
      }}>
        {this.props.children}
      </MyContext.Provider>
    )
  }
}

const Family = (props) => (
  <div className="family">
    <Person />
  </div>
)

class Person extends Component {
  render() {
    return (
      <div className="person">
        <MyContext.Consumer>
          {(context) => (
            <React.Fragment>
              <p>Age: {context.state.age}</p>
              <p>Name: {context.state.name}</p>
              <button onClick={context.growAYearOlder}>🍰🍥🎂</button>
            </React.Fragment>
          )}
        </MyContext.Consumer>
      </div>
    )
  }
}


class App extends Component {
  render() {
    return (
      <MyProvider>
        <div>
          <p>I am the app</p>
          <Family />
        </div>
      </MyProvider>
    );
  }
}


export default App;

When To Use Context

There are two use cases when to use it:

  • When your React component hierarchy grows vertically in size and you want to be able to pass props to child components without bothering components in between.
  • When you want to have advanced state management in React with React Hooks for passing state and state updater functions via React Context through your React application. Doing it via React Context allows you to create a shared and global state.

If you have any question regarding this or anything I should add, correct or remove, feel free to comment, email or DM me. Thanks !!!

Top comments (0)