DEV Community

Cover image for useContext() with Typescript
Amateur blogger
Amateur blogger

Posted on • Updated on

useContext() with Typescript

The below article will give you an overview with an example of how to use the useContext() hook and also update the globally set context value in child components.

Prerequisites: Basic familiarity with React and Typescript

Usually, in a React application, data is passed top-down (parent to child) via props. The reason third party state management libraries like Redux became so popular is due to the prop drilling problem of React.

Prop drilling consists of passing a prop to the nested child components, and as a result, children who do not need this prop, still end up getting it even though they might never use it.

useContext() hook makes it easy to pass data throughout your app without manually passing props down the tree. It allows to create a global state and the required child components can consume it.

useContext() can prove to be a simple alternative to other state management libraries if your data is not complicated and the application is small.

This is what the process involves:

  1. Create a context object by using React.createContext()
  2. Provide the globally created context to your child >components using Provider

Example using Typescript:

In the below example, I want to set the value of content globally based on the user type passed which can be consumed by the nested child components. I will also update the value of content in one of the nested child component.

Let’s get started 🙌

  • Initialise, the context at a top-level using a default value. Create a global context hook useGlobalContext()

I have created a GlobalContent type which has copy and a setCopy(). The reason for using setCopy() will be clarified later.

import { createContext, useContext } from "react"
export type GlobalContent = {
  copy: string
  setCopy:(c: string) => void
}
export const MyGlobalContext = createContext<GlobalContent>({
copy: 'Hello World', // set a default value
setCopy: () => {},
})
export const useGlobalContext = () => useContext(MyGlobalContext)
Enter fullscreen mode Exit fullscreen mode
  • Wrap the context at the parent level to which the value of context would be accessible for its child components. I have wrapped it around the child components in App.tsx

In the below example, the getCopy() is getting the copy based on the logged in Admin user and making it available for the child components Home and About

import { MyGlobalContext } from './MyGlobalContext'
import React, { useState } from 'react'
import { getCopy } from './Content'
import { Home } from './Home'
import { About } from './About'
function App() {
const [copy, setCopy] = useState<string>(getCopy('Admin'))
return 
  (
   <MyGlobalContext.Provider value= {{ copy, setCopy }}>
     <Home/> 
    <About/>
  </MyGlobalContext.Provider>
  )
}
export default App
Enter fullscreen mode Exit fullscreen mode
  • Utilise the globally set value of copy in your child component Home.tsx
import { useGlobalContext } from './MyGlobalContext'
const Home = () => {
const { copy } = useGlobalContext()
return <div>{copy}</div>
}
export default Home
Enter fullscreen mode Exit fullscreen mode

That is it!!!! Your useContext() hook is in use and you have successfully set the value of copy i.e. content globally 😄.

You must be wondering why do we still have an unused setCopy() 🤔

As I mentioned at the beginning of the article, if you ever want to update the value of copy in child components, you need to pass a function to update the copy. In our case, setCopy() will allow you to update the copy.

In the below example, I am setting a new value for copy on the click of a button and passing it to the nested child component MyProfile.tsx

import { useGlobalContext } from './MyGlobalContext'
import { MyProfile } from './MyProfile'
const About = () => {
const { copy, setCopy } = useGlobalContext()
return(
   <>
    <button onClick={() => setCopy('This is a new copy')}>
      Click me!
    </button>
    <MyProfile newContent={copy} />
  </>
 )
}
export default About
Enter fullscreen mode Exit fullscreen mode

That’s it. Believe it or not, with what you learned above (or parts of it, really), you can start using useContext() hook and create a global state for your application.

Top comments (14)

Collapse
 
jdgamble555 profile image
Jonathan Gamble

For me, I had to add an empty _value like:

export const DarkContext = createContext({ 
  darkMode: false,
  setDarkMode: (_value: boolean) => { }
});

export const useDarkContext = () => useContext(DarkContext);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
typhon0130 profile image
Hatake Honzo

Good working

Collapse
 
jedijetskyjodajedoindajaus profile image
jedidiah

It doesn't work for me, i don't have a getCopy, what this function does? could you please help me or get it touch with me!

Collapse
 
madv profile image
Amateur blogger

Hey! Thank you for your message and sorry for the inconvenience caused.
Basically, getCopy() method could be anything whose result you want to set globally. In my case, it is a simple method which gets content based on the logged in user type.
For eg:

const getCopy = (userType: string):string => 
{
   if (userType.toLowerCase() === 'admin')
   {
      return 'Hello Admin User!'
   }
   return 'Welcome user!'
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
enough7 profile image
Enough7 • Edited

You can use localstorage for this:

const USER_ID_KEY: string = "userId";
export let thisUser: IUser = {
    id: parseInt(localStorage.getItem(USER_ID_KEY) || '-1') 
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
madv profile image
Amateur blogger

Thank you for the alternative, however what if the user has disabled his localStorage? ;)

Thread Thread
 
enough7 profile image
Enough7

I did not know you can disable it

Collapse
 
ofergal profile image
Ofer Gal

How will you change this when the objects to set and get are much more complex?
I have something like :
export interface IDocumentInfo {
file: IFilePickerResult;
pages?: number;
instructions?: string;
existsOnsite?: boolean;
}
export interface IRequestInfo {
projectNumber: string;
documents: IDocumentInfo[];
}

and I want a global context for an IRequestInfo object.
Thank you

Collapse
 
ben profile image
Ben Halpern

Nice post

Collapse
 
madv profile image
Amateur blogger

Thanks a lot Ben :). Being my first technical post, I am glad you liked it :)

Collapse
 
raj__ profile image
Info Comment hidden by post author - thread only accessible via permalink
Raj

medium.com/@masterrajpatel/useglob...

tried to explain the same , see if it can help others

Collapse
 
koaladlt profile image
Manuel de la Torre

Thanks a lot! I started lo learn Typescript recently and you save me a lot of time. I would buy you a beer if I could. Cheers!

Collapse
 
madv profile image
Amateur blogger

haha, that is really sweet Manuel! Glad, I was able to help :)

Collapse
 
madakhamjonov profile image
m-adakhamjonov

Men o'zimga shuncha oldim.

Some comments have been hidden by the post's author - find out more