Using the useContext hook in React
Tags: react, javascript, typescript, props
Published: 2022-07-24 13:55:10

Sometimes when you're building an app in React, it can be difficult to pass props from one component to another without prop drilling. One of the in-built tools that React has to tackle this issue is the useContext hook.

The React's Context allows you to wrap your components in a context provider. The values which you need to share throughout your components can then be initialised at the top level and then accessed in any component using the useContext hook.

Let's take a look at a simple example.

Let's say we have an App which contains two components - one is a text input and the other is a component which will display the value that the user enters.

![user types name in input field and it displays below](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r39sf7bgvh6er2nrlp88.gif)

Here's the file structure.

![App component has two child components - Input and Result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q45ew9nmbtozqrkkpp4j.jpg)

Unfortunately, we can't simply pass the input value between the Input and Result siblings. In reality, in this very simple example, the best way of dealing with the issue would be to lift the state to the App component and then pass it down to each of the child components. But let's say you were building a more complex app and you needed to pass the state down several levels of the component tree while avoiding prop drilling - that's where Context comes in.

The starter files for this example can be found [here](https://github.com/LauraToddCodes/using-usecontext/tree/starting-code).

First, we want to create a new file and create our context using `React.createContext`.

```
import * as React from "react"

export type InputValues = {
  nameValue: string
  setNameValue: React.Dispatch<React.SetStateAction<string>>
}

export const InputContext = React.createContext<InputValues>({
  nameValue: "",
  setNameValue: () => console.info("Name not yet initialised"),
})
```

In the createContext object, you will need to add and initialise any values that you need to. Here, we've set up `nameValue` which will be used in the Result component to display the name and the `setNameValue` which will be used to set the value in the Input component.

Next, we'll create our own hook which we can use later in the provider. 

```
import * as React from "react"

import { InputValues } from "./input-context"

export function useInputProvider(): InputValues {
  const [nameValue, setNameValue] = React.useState("")

  return {
    nameValue,
    setNameValue,
  }
}
```

Here, we simply set up `nameValue` and `setNameValue` with the React useState hook and return them to use in our next step.

Now we need to go to our App file and wrap our Input and Result components in a context provider.

```
import { Input, Result } from "./components"

import { InputContext } from "./context"
import { useInputProvider } from "./context/use-input-provider"

function App() {
  const nameValue = useInputProvider()

  return (
    <div className="inputForm">
      <InputContext.Provider value={nameValue}>
        <Input />
        <Result />
      </InputContext.Provider>
    </div>
  )
}

export default App
```

So, we use the `InputContext` that we set up in the first step and wrap the child components in the provider. We can then use the `useInputProvider` hook that we set up in the second step to pass `nameValue` and `setNameValue` as the Provider value.

Now that we've set up the provider, how do we access the values in our child components?

First, let's go to our Input component. 

```
import * as React from "react"
import { InputContext } from "../context"

export function Input(): JSX.Element {
  const { setNameValue } = React.useContext(InputContext)

  function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
    setNameValue(e.target.value)
  }

  return (
    <form>
      <label htmlFor="name">Name: </label>
      <input type="text" id="name" name="name" onChange={handleChange} />
    </form>
  )
}
```

Here, we need to access `setNameValue` in order to set the name value to whatever the user types in the input field. To do this, we can use the `useContext` hook and pass in our `InputContext`. You can then extract `setNameValue` like this -
```
const { setNameValue } = React.useContext(InputContext)
```

You can then go ahead and use `setNameValue` to take in the input value.

Finally, let's go over to our Result component and access `nameValue` in the same way using `useContext`.

```
import * as React from "react"
import { InputContext } from "../context"

export function Result() {
  const { nameValue } = React.useContext(InputContext)
  return <div>{nameValue}</div>
}
```

We can then pass the `nameValue` into the `<div>` to display the result.

And that's it! You can find the completed code [here](https://github.com/LauraToddCodes/using-usecontext/tree/main).