No Time Dad

A blog about web development written by a busy dad

React useContext TypeScript Example

React’s useContext hook is a great place to store information about an authenticated user. I do this all the time. Knowing the authentication status is something most apps will need at a global level. Which is exactly what the React context is great for.

The useContext hook example from the react docs is great, but I wanted to take things a step further and create a useContext TypeScript example. I’ve been spending a ton of time with TypeScript lately, so it only made sense to create a simple example with it for a common requirement in React apps, user authentication status. Below is a small example component that uses TypeScript with the useContext hook.

import { createContext, useContext } from "react"

type User = {
  username: string
  isAuthenticated: boolean
}

type Context = {
  theme: "light" | "dark"
  user?: User
}

const appContext: Context = {
  theme: "dark",
  user: { username: "Joe", isAuthenticated: true },
}

export const GlobalContext = createContext(appContext)

const App = () => {
  const context = useContext(GlobalContext)
  return (
    <>
      <div>Awesome App!</div>
      {context.user && context.user.isAuthenticated && (
        <div>Hello {context.user.username}!</div>
      )}
    </>
  )
}

export default App

The basic idea here is that I’ve created two types for my context object. A User type for information about the authenticated user, and a Context type for the context object itself that’ll be passed to the useContext hook. It’s worth pointing out that I’ve made the user property on the Context object optional. This is because the app might not always have a logged in user, and I’ll want to check for that scenario throughout the app.

Once I have the types defined I can create a context object using the Context type and pass it to the createContext function, which creates the context that can be used or imported as needed throughout the app. In this example I created the context in the App.tsx component file, but in a larger app I might put it in it’s own file and directory.

Accessing the context in the component requires me to use the useContext hook, passing the Context as an argument to it. It’s common to see useContext TypeScript examples where the type is added to the useContext function (as shown below), but the TypeScript compiler has likely already inferred the type, so that might not always be necessary.

// Common to see the type added like this
const context = useContext<Context>(GlobalContext)

But hovering over the declaration in the original version at the start of this post would show that TypeScript already knows the type (as shown below). It wouldn’t hurt anything per say to add the type as shown above, but it’s not required. The benefit of adding the type as shown above would be for easier understanding at a glance if someone else was looking at the code.

/*
  const context: Context

  (alias) useContext<Context>(context: React.Context<Context>): Context
  import useContext
**/
const context = useContext(Context)

The last thing to do in this component is to use the context object. TypeScript does some extra leg work for us here, making sure that we are doing the proper checks when accessing the object. The code shown below would result in an error from the compiler.

// Object is possibly 'undefined'.ts(2532)
<div>Hello {context.user.username}!</div>

The user property is optional in the Context object because there might not be an authenticated user. This means that I need to explicitly check for the user property, then I can check the value of the isAuthenticated and username properties. I can use the && operator to accomplish this in React.

{
  context.user && context.user.isAuthenticated && (
    <div>Hello {context.user.username}!</div>
  )
}

This is exactly what’s so great about TypeScript and why I’ve been trying to move all of my projects, past and future to it. That extra level of type safety and security is so nice.