Author avatar

Benney Au

Using React's Context API with Typescript

Benney Au

  • Sep 15, 2020
  • 5 Min read
  • 3,163 Views
  • Sep 15, 2020
  • 5 Min read
  • 3,163 Views
Web Development
Front End Web Development
Client-side Frameworks
React

Introduction

For most React components, you provide data to a child component by manually passing down props through every level of the component hierarchy. When you have a deeply nested hierarchy tree, this becomes cumbersome. React offers the context API as a solution to this issue. You will want to use this over a global variable so that React knows when to re-render your components.

In this guide, you will learn how to use strongly typed React contexts with TypeScript. We will focus on using the Context API inside function components with React Hooks since React Hooks are recommended for new feature development. Throughout this guide, we will use an example that involves storing whether the user has selected a dark or light theme in the context and consuming this context from a deeply nested component. A basic understanding of React Hooks is assumed knowledge for this guide. You can watch this Using React Hooks course if this pattern is new to you.

Set Up a TypeScript React Project

Start by creating a new React project by running the following commands:

1
2
3
4
npx create-react-app my-app
cd my-app
yarn add typescript
yarn start

These commands will set up a new project, install TypeScript, and start the local development server.

Create the Context Type Definitions

In order to take advantage of TypeScript, you need to define some types for it to work with. For the purpose of this guide, make the current selected theme and a function that sets the theme available to context consumers.

To do this, create a new file, src/ThemeContext.ts, and add the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { createContext, useContext } from 'react';

export enum Theme {
    Dark = 'Dark',
    Light = 'Light',
}

export type ThemeContextType = {
    theme: Theme;
    setTheme: (Theme: Theme) => void;
}

export const ThemeContext = createContext<ThemeContextType>({ theme: Theme.Dark, setTheme: theme => console.warn('no theme provider')});
export const useTheme = () => useContext(ThemeContext);
typescript

There are four definitions in this file:

  • An enum that describes all the possible themes that a user can select. Note that the underlying type is represented as a string. This is easier to debug but still type-safe.
  • ThemeContextType is a second type representing what you can access from the context. It contains the two pieces of data mentioned earlier.
  • The ThemeContext is an object that we will use to provide a context. Here you declare type associated with this context as <ThemeContextType> and its default values. These default values are returned to consumers when there is no theme provider.
  • Finally, useTheme is a custom hook to make consuming the theme and its setter function more convenient. By convention, all hooks are prefixed with use—that's why it is called useTheme.

Use the ThemeContext Provider

You have just created the building blocks for providing and consuming a theme state. Now start providing a theme by wrapping it around the root of your component hierarchy.

First, rename src/App.js to src/App.tsx.

Then change its content to the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import * as React from 'react';
import './App.css';
import { ThemeContext, Theme } from './ThemeContext';
import MyPage from './MyPage';

function App() {
  const [theme, setTheme] = React.useState(Theme.Light);

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <div className="App">
        <header className="App-header">
          <MyPage />
        </header>
      </div>
    </ThemeContext.Provider>
  );
}

export default App;
typescript

You have wrapped the entire App component with the ThemeContext.Provider. This means that the theme should be accessible from anywhere in the app. The entire app will also be re-rendered if the values in the theme context changes, so it's important not to add frequently changing variables to this context.

Consume the Context From a Nested Component

To consume the theme, use the useTheme hook that you defined earlier. Create a new file, src/MyPage.tsx, and add the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import * as React from 'react';
import { useTheme, Theme } from './ThemeContext';

const MyPage = () => {
  const { theme, setTheme } = useTheme();
  console.log(theme);

  return (
    <div>
      <button onClick={() => setTheme(Theme.Dark)}>
        switch to dark theme
      </button>
        my page
      </div>
  )
}

export default MyPage;
typescript

Notice that you have access to the theme and setTheme variables without having to pass those props down manually. In a large app, this can be much more convenient. The theme and setTheme variables are also typed, which is useful for catching bugs.

Conclusion

This guide has demonstrated how to use React's context API with TypeScript to make theme and setTheme globally available and easy to consume. As a final note, you should remember that contexts don't need to be global, and this is not the solution for every state-sharing problem.

16