DEV Community 👩‍💻👨‍💻

Cover image for Using the useContext hook in React
Laura Todd
Laura Todd

Posted on

Using the useContext hook in React

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

Here's the file structure.

App component has two child components - Input and Result

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.

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"),
})
Enter fullscreen mode Exit fullscreen mode

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,
  }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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>
  )
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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>
}
Enter fullscreen mode Exit fullscreen mode

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

And that's it! You can find the completed code here.

Top comments (0)

In defense of the modern web

I expect I'll annoy everyone with this post: the anti-JavaScript crusaders, justly aghast at how much of the stuff we slather onto modern websites; the people arguing the web is a broken platform for interactive applications anyway and we should start over;

React users; the old guard with their artisanal JS and hand authored HTML; and Tom MacWright, someone I've admired from afar since I first became aware of his work on Mapbox many years ago. But I guess that's the price of having opinions.