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.
Start by creating a new React project by running the following commands:
1npx create-react-app my-app
2cd my-app
3yarn add typescript
4yarn start
These commands will set up a new project, install TypeScript, and start the local development server.
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:
1import { createContext, useContext } from 'react';
2
3export enum Theme {
4 Dark = 'Dark',
5 Light = 'Light',
6}
7
8export type ThemeContextType = {
9 theme: Theme;
10 setTheme: (Theme: Theme) => void;
11}
12
13export const ThemeContext = createContext<ThemeContextType>({ theme: Theme.Dark, setTheme: theme => console.warn('no theme provider')});
14export const useTheme = () => useContext(ThemeContext);
There are four definitions in this file:
ThemeContextType
is a second type representing what you can access from the context. It contains the two pieces of data mentioned earlier.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.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
.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:
1import * as React from 'react';
2import './App.css';
3import { ThemeContext, Theme } from './ThemeContext';
4import MyPage from './MyPage';
5
6function App() {
7 const [theme, setTheme] = React.useState(Theme.Light);
8
9 return (
10 <ThemeContext.Provider value={{ theme, setTheme }}>
11 <div className="App">
12 <header className="App-header">
13 <MyPage />
14 </header>
15 </div>
16 </ThemeContext.Provider>
17 );
18}
19
20export default App;
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.
To consume the theme, use the useTheme
hook that you defined earlier.
Create a new file, src/MyPage.tsx
, and add the following code:
1import * as React from 'react';
2import { useTheme, Theme } from './ThemeContext';
3
4const MyPage = () => {
5 const { theme, setTheme } = useTheme();
6 console.log(theme);
7
8 return (
9 <div>
10 <button onClick={() => setTheme(Theme.Dark)}>
11 switch to dark theme
12 </button>
13 my page
14 </div>
15 )
16}
17
18export default MyPage;
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.
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.