What is React Context And How To Use It?
Tags: javascript, react, webdev
Published: 2019-12-31 10:36:24

<p>If you're having a problem with passing property to a component just to pass it further down to child, React Context is exactly what you need.</p>
<p>By the definition, React Context provides us a possibility to pass data through the component tree, so you don’t need to pass props down manually at every level. </p>
<p>In other words, we can compare context to a global object of our React App.</p>
<h2><b>Prop drilling problem</b></h2>
<img src="https://blog.pagepro.co/wp-content/uploads/2019/12/pasted-image-0-1024x515.png" alt="Tree structure components without context" class="wp-image-2409"/>
<p>React components structure is like a tree. Each child has only one parent and everyone is connected to the main root component. Thanks to this structure we have only one direction flow — we can pass props from top to bottom. </p>
<p>When we need to pass prop through a lot of components (f.ex from root to A3) it becomes a little bit annoying, and we called it a prop drilling problem. React Context comes to the rescue.</p>
<img src="https://blog.pagepro.co/wp-content/uploads/2019/12/withcontext.png" alt="Tree structure components with context" class="wp-image-2410"/>
<p>When we need to make some of the data global in our app, or we would like to use them in a few components on a different deeply nested levels in the app structure, we definitely should use React Context. </p>
<p>It gives us access to the data on each level of our React App tree structure.</p>
<h2><b>How to create Context?</b></h2>
<p>The way of creating context is to import <code>createContext</code> method from React library and invoke it with <code>defaultValue</code> - it is not required but can be helpful when a component has not matched Provider in the tree. </p>
<p>Moreover, using <code>defaultValue</code> during creating React Context is important in testing component as separated from others.</p>
```javascript
import { createContext } from 'react'
createContext('defaultValue')
```
<h2><b>Example of creating Context</b></h2>
```javascript
export const CountryContext = createContext({})
export const LanguageContext = createContext('en')
```
<p><strong>TIP:</strong> Good practise is to have separate file for creating contexts.</p>
<h2><b>How can we pass down Context?</b></h2>
<p>Create Context method returns an object with Provider and Consumer. </p>
<p>Thanks to Provider we can pass props down in our app structure. Provider component has a prop - <code>value</code> - which allows us to pass data assigned to this prop to all descendants <em>(in <code>value</code> we can pass an object, number, function etc...)</em>. One Provider can be connected to many consumers. </p>
<p>Furthermore, Provider can be nested, thanks to that we can override passed data in <code>value</code> prop deeper within the app. </p>
<p>If <code>value</code> prop changes all consumers of a Provider will re-rendered.</p>
```javascript
const { Provider } = createContext('defaultValue')
```
<h2><b>Example of using Provider</b></h2>
```javascript
<CountryContext.Provider
  value={{
     setSelectedCountry,
     selectedCountry
   }}
>
  <LanguageContext.Provider
    value={{
       lang: selectedLanguage,
       setSelectedLanguage
     }}
  >
    <header> ...
    <main> ...
    <footer>... 
  <LanguageContext.Provider>
</CountryContext.Provider>
```
<h2><b>How can we get Context?</b></h2>
<p>We can have access to data which we passed to <code>value</code> prop in Provider thanks to subscriber called Consumer. </p>
<p>The Consumer component requires a function as a child that has the context current value in an argument and returns a React Node element. </p>
```javascript
const { Consumer } = createContext('defaultValue')
```
<h2><b>Example of using context by Consumer</b></h2>
```javascript
<CountryContext.Consumer>
  {({ selectedCountry }) => (
    <h1>
      {selectedCountry.name}
    </h1>
  )}
</CountryContext.Consumer>
```
<p>In this example we use <code>CountryContext</code> to have access to selected country. We create function returning country name which we received in an argument of it <em>(the newest applied context)</em>.</p>
<h2><b>Example of using Context Consumer as a hook</b></h2>
```javascript
import React, { useState, useContext } from 'react'
import axios from 'axios'
import { CountryContext } from './contexts'
import { pushErrorNotification } from './utils'

const SearchBox = () => {
  const [searchValue, setSearchValue] = useState('')
  const {
    setSelectedCountry
  } = useContext(CountryContext)

  const searchCountry = () => {
    axios.get(`${endpoint}${searchValue}`)
      .then(({ data }) => {
        setSelectedCountry(data)
      })
      .catch(() => pushErrorNotification('Sth went wrong.'))
  }

  return (
    <div className="search-wrapper">
      <input
        type="text"
        id="search"
        name="search"
        value={searchValue}
        placeholder="Search for..."
        onChange={({ target }) => setSearchValue(target.value)}
      />
      <button onClick={() => searchCountry()}>
        Search
      </button>
    </div>  
  )
}

export default SearchBox
```
<p>Here we have a <code>SearchBox</code> component where we can type desirable country name and find some info about it. Thanks to <code>useContext</code> hook, we can quickly set found country on current displaying details by <code>setSelectedCountry</code> method.</p>
<h2><b>Easy access to Context</b></h2>
<p>In the documentation, we can read that: </p>
<p><em>The contextType property on a class can be assigned a Context object created by React.createContext(). </em></p>
<p><em>This lets you consume the nearest current value of that Context type using this.context. You can reference this in any of the lifecycle methods including the render function.</em></p>
```javascript
ComponentA.contextType = ContextB
OR
static contextType = ContextB
```
<h2><b>Example of using context by ‘this’</b></h2>
```javascript
static contextType = CountryContext

render () {
  const {
    selectedCountry,
    selectedCountry: {
      borders = []
    }
   } = this.context
}
```
```javascript
import React from 'react'
import { CountryContext } from './contexts'

class CountryDetails extends React.Component {  
  render () {
    const {
       selectedCountry: {
         capital,
         region,
         subregion,
         area,
         population,
         timezones = []
       }
     } = this.context

     return (
       <div> ...
     )
  }  
}

CountryDetails.contextType = CountryContext

export default CountryDetails
```
<h2><b>Make work/debugging faster</b></h2>
```javacript
CountryContext.displayName = 'SelectedCountry'
```
<img src="https://blog.pagepro.co/wp-content/uploads/2019/12/context-1.png" alt="context.provider app layout" class="wp-image-2419"/>
<h2><b>Example of using multiple contexts </b></h2>
```javascript
import React, { useContext } from 'react'
import { CountryContext, LanguageContext } from './contexts'


// using hook in stateless components
const Languages = () => {  
    const {
        selectedCountry: {
            languages = []
        }
    } = useContext(CountryContext)

    const {
        lang
    } = useContext(LanguageContext)

    return (
        <div>...
    )
}
```
```javascript
// using Consumer component f.ex. in class components
<CountryContext.Consumer>
  {({ selectedCountry }) => (
    <LanguageContext.Consumer>
      {({ lang }) => {
	      <div> ...
        }
	  }
    </LanguageContext.Consumer>
  )}
 </CountryContext.Consumer>
```
<h2><b>Summary</b></h2>
<p>React Context is a very approachable and helpful API for managing state over multiple components. </p>
<p>React Context is a very approachable and helpful API for managing state over multiple components. </p>
<p>It makes our work faster and easier by accessing data everywhere across the app.</p>