How to Use Context with React Hooks
Tags: react, javascript, codenewbie, tutorial
Published: 2020-08-06 00:37:11

[Context](https://reactjs.org/docs/context.html) is probably my favorite React feature, especially when using hooks. It’s not bleeding edge tech anymore, so you should take a second to learn how it works. We’re just going to create a Context component, and then read/set values from it in our main app. It’s going to be a very simple project, but one that shows the basics and how you can build on it in the future. Here is the [code on GitHub.](https://github.com/MostlyFocusedMike/context-template)

# What is Context?
Context lets you have global properties and functions that can be accessed from anywhere in your project. This is pretty much what Redux does, and the best way to distinguish Redux from Context is size: Context is smaller and simpler. The model for a Redux Store is usually a intricate, immutable object, whereas with Context it would be more helpful if you think about it like a floating component that can talk to any other component. You also don't have to use reducers, which can also drastically simplify things.

### Setup
Use create-react-app and that's it. We aren't going to have any external dependencies. We're going to create a Context component, give it an internal state, and then share that state with the rest of our app. All our app is actually going to *do* is save an input string to Context. I encourage you to read them all the same. It's good to know both the hooks and state version, since your company may not be using the latest React.

# Step 1: Create a Context component
In `src/` create a `context/` directory and inside it put `index.js` and `ContextProvider.js`. Let's fill out `index.js` first:

```js
import React from 'react';
const AppContext = React.createContext({});
export default AppContext;
```

I'm going to explain that second line, but first let's also create `ContextProvider.js`:

```js
import React, { useState } from 'react';
import AppContext from '.';

const ContextProvider = ({ children }) => {
  const [example, setExample] = useState('Hello there')
  const context = {
    setExample,
    example,
  };
  return (
    <AppContext.Provider value={ context }> 
      {children}
    </AppContext.Provider>
  );
}

export default ContextProvider;
```

# Step 1a: What did we do
Alright, let's talk about `createContext` and `AppContext.Provider`. We actually create our Context in `index.js`, this is the "raw" Context if you will. See, context itself is really just a value, but React incorporates it into its system and gives it `Consumer` and `Provider` components. Now, hooks let us bypass the need for a `Consumer` component, but we still need to have a parent Provider component.

What our `Provider` component does is take a `value` (we're calling it `context`, but it can be named anything) and then make it accessible to any of the children components. This `value` is our global store. Also, if you aren't familiar with `children` props, we'll talk about it in the next step.

### Internal state
Notice what we pass into our `context` value: it's a `useState` hook and its accompanying value. That's the best part about this setup, we're simply tracking a regular component's state. When an external component needs to update the store, there's no magic, it's merely updating the internal state of the Context component. That change is then updated wherever it gets read, like a different version of props. There's nothing new here except *where* the data is being stored. You can of course add as much as you like to this object, but for now we're keeping it pretty bare bones.

# Step 2: Plug your Context into your app
In order for Context to do anything, we need to make it available. Any child component of our `ContextProvider` component will have access to the store. That means, we need to put it somewhere very high up in the component chain, so I usually put it at the top in the `src/index.js` file:

```js
import React from 'react';
import ReactDOM from 'react-dom';
import ContextProvider from './context/ContextProvider';
import App from './App';

ReactDOM.render(
  <React.StrictMode>
    <ContextProvider>
      <App />
    </ContextProvider>
  </React.StrictMode>,
  document.getElementById('root')
);
```

This is also where the children prop in our `ContextProvider` comes into play. Recall our return statement in our provider component:

```js
return (
  <AppContext.Provider value={ context }>
    {children}
  </AppContext.Provider>
);
```

By nesting out `<App>` inside `<ContextPrivider>`, our main app and all its child components are now the children of the `<AppContext.Provider>` component. That's what actually lets our app get access to our Context and prevents unnecessary renders. Here's a [quick article on props.children](https://medium.com/javascript-in-plain-english/how-to-use-props-children-in-react-7d6ab5836c9d) if you aren't familiar with it.


# Step 3: Use your Context in a component
Alright, here we go! All we're going to make is a little form that lets us set the string value of `example` in our Context. And we'll display it with the `useEffect` hook and a console log. We're going to keep things simple and do it all in our main `src/app.js` file:

```js
import React, { useContext, useState, useEffect } from 'react';
import './App.css';
import AppContext from './context';
const App = () => {
  const { example, setExample } = useContext(AppContext);
  const [formText, setFormText] = useState('');
  useEffect(() => {
    console.log('context here: ', example);
  }, [example]);
 
  const handleChange = (e) => {
    setFormText(e.target.value);
  };
  const handleSubmit = (e) => {
    e.preventDefault();
    setExample(formText);
  };
  
  return (
    <div className="App">
    <form onSubmit={handleSubmit}>
      <label htmlFor="example">Example: </label>
      <input
        type='text'
        value={formText}
        onChange={handleChange}
      />
      <button>DO IT</button>
    </form>
    </div>
  );
};
export default App;
```

There's the whole thing, and here are the parts that use Context:

```js
import AppContext from './context'; 
// ...
const App = () => {
  const { example, setExample } = useContext(AppContext);
  useEffect(() => {
    console.log('context here: ', example);
  }, [example]);
// ...
  const handleSubmit = (e) => {
    e.preventDefault();
    setExample(formText);
  };
  
  return (
// ...
      <input
        type='text'
        value={formText}
        onChange={handleChange}
      />
```

We just feed our Context into the `useContext` hook and then pull out the properties we want to use. The way you use those properties is pretty much the same as you would a `useState` function or value. Remember the Context object is the one defined in `index` not the `ContextProvider` component, that's only ever used in a single place. This is surprisingly simple, but that's all thanks to hooks. They all work together seamlessly that Context really fits right in.

# That's pretty much it
There used to be a bit more pomp and circumstance when using Context with class based components, but hooks make it like another useState (if you need class based components, check out [Wes Bos's tutorial](https://www.youtube.com/watch?v=XLJN4JfniH4), I just adapted the pattern for hooks). And of course things can get more complex, like multiple Contexts or a `useReducer` instead of `useState`, but at it's core, it's a simple concept.

happy coding everyone,
mike