React useContext TypeScript Example
March 25, 2022
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.