React Context
Tags: react
Published: 2020-04-10 01:03:53

I'm almost two weeks into learning React, and while working on a [lab that models Westworld's command center](https://www.youtube.com/watch?v=GhCazAgsJzw&feature=youtu.be) I realized that I was passing props down five levels deep! This process of *prop drilling* (vocab word of the day) struck me as a lot of extra typing, and therefore not terribly React-like. For example, information about my hosts was stored in state the top level in App, and passed down into my next components:
```react
//In App
render(){
    return (
      <Segment id='app'>
        ...
        <Headquarters hosts={this.state.hosts} ... />
      </Segment>
    )
  }
```
where it was passed deeper...
```react
// in Headquarters
<ColdStorage hosts={this.hostsInStorage()} ... />
```
and deeper...
```react
// in ColdStorage
<HostList hosts={props.hosts} ... />
```
still deeper...
```react
// in HostList
{props.hosts.map(host => {
        return <Host key={host.id} host={host} ... />
        })}
```
and finally down in the Host component the information is actually used.

That's a lot of repetitive typing for every prop I might need to send from App down to a Host. While there are multiple solutions to this problem, React Context offers one. Straight from the [React docs:](https://reactjs.org/docs/context.html)
> Context provides a way to pass data through the component tree without having to pass props down manually at every level.

Stated this way, Context offers a solution to the exact problem I encountered. So, onto adding Context.

In my Westworld lab, I want to create a Context that contains my Host data and makes it available to all of the children. The easiest way for me to do this with my current code is to create a new Context in App;
```javascript
const hostContext = React.createContext({host: []});
```
I'm initially creating a Context with a default value of `{host: []}` because my host data is fetched asynchronously in `componentDidMount` in App. To make this Context available to all of App's children, I'll use React's Provider component, a property of the Context. I'll wrap all of App's children in `<hostContext.Provider value={importantDataHere}>` tags. 
The `value` prop is what contains the information that I want passed down to all of my children components. In this case, I can assign `value={hosts: this.state.hosts}` where `this.state.hosts` now contains all of my fetched data. To recap, my App component will look like this:
```react
...
const hostContext = React.createContext({host: []});
...

class App extends Component {

  state = {
    hosts: [],
    ...
  }

  componentDidMount() {
    fetch(HOSTS_URL)
    .then(resp => resp.json())
    .then(data => this.setState({
      hosts: data
    }))

  render(){
    return (
     <hostContext.Provider value={{hosts: this.state.host}}>
      <Segment id='app'>
        ...
        <Headquarters />
      </Segment>
     </hostContext.Provider>
    )
  }
}
```
This is great! But how do the children of App (and my new hostContext) access the value passed by the Provider? For class based components, the `contextType` property can be assigned to the given Context, and the value accessed using `this.context`:
```javascript
class Headquarters extends Component {
...
const hosts = this.context.hosts;
...
}
Headquarters.contextType = hostContext;
```
Note that only one Context can be assigned to a class based component in this way. 

For functional components, the hook `useContext` gives access in the same way that `this.context` does for class components, and no need to include any assignment of `contextType`:
```javascript
const hosts = useContext(hostContext).hosts
```
There are plenty of pros and cons to Context and it's not the right choice for every situation, but it seems like a great tool to have in the toolbox!