Laura Todd
Laura Todd Codes

Laura Todd Codes

Using the useContext hook in React

Using the useContext hook in React

Laura Todd's photo
Laura Todd
·Jul 25, 2022·

4 min read

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"),
})

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.

 
Share this