React Context with TypeScript: Part 2 - Complex context with function components
Tags: react, typescript
Published: 2020-02-25 20:45:21

---
title: React Context with TypeScript: Part 2 - Complex context with function components
published: true
description: How to use TypeScript to create a strongly-typed React context for function components where the context can be changed by the consumer
tags: react,typescript
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/e9o0s3wupeiuxn3wb5ai.jpg
canonical_url: https://www.carlrippon.com/react-context-with-typescript-p2/
---

This is the second post in a series of posts on [React context](https://reactjs.org/docs/context.html) with TypeScript. In the [last post](https://www.carlrippon.com/react-context-with-typescript-p1/), we created a simple context where TypeScript was able to infer its type.

- Part 1 - [Simple context with function components](https://www.carlrippon.com/react-context-with-typescript-p1/)
- Part 2 - [Complex context with function components](https://www.carlrippon.com/react-context-with-typescript-p2/) (this post)
- Part 3 - Context with class components (coming soon ...)
- Part 4 - Creating a context with no default and no undefined check (coming soon ...)

In this post, we will implement a more complex context where we need to specify the context type explicitly.

## Explicitly setting the context type

We are going to enhance the context from the last post so that the theme can be updated by consumers.

In the last post, the type for the context was inferred from the default value, which was a simple `string`. The type for our enhanced context is going to be a little more complex:

```typescript
type ThemeContextType = {
  theme: string;
  setTheme: (value: string) => void;
};
```

So, there will be a `theme` property containing the current value for the theme and a `setTheme` method to update the current theme.

Reacts `createContext` function expects us to supply an argument for initial context value. We can supply a default value for the `theme` property, but it doesn't make sense to provide a default implementation for the `setTheme` method. So, a simple approach is to pass in `undefined` as the initial value:

```typescript
const ThemeContext = React.createContext(
  undefined
);
```

Let's see what the inferred type is for the context value:

![Inferred context type](https://dev-to-uploads.s3.amazonaws.com/i/687uk8lchovas88ruj0r.png)

The type of the context value is inferred to be `undefined` if in strict mode or `any` if not.

So, `ThemeContext` isn't typed as we require at the moment. How can we explicitly specify the type for the context when using `createContext`? Well, `createContext` is a generic function. So, we can pass in the type for the context value as a generic parameter:

```typescript
const context = React.createContext<ContextType>(...)
```

Therefore, we can type our context is as follows:

```typescript
const ThemeContext = React.createContext<
  ThemeContextType | undefined
>(undefined);
```

## Using the enhanced context in the provider

The modification to the context provider, from the last post, is to the value we provide from it. Instead of a simple `string`, it is now an object containing the `theme` property and the `setTheme` method:

```jsx
export const ThemeProvider = ({
  children
}: Props) => {
  ...

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};
```

## Adding an option to change the theme in the `Header` component

The `useTheme` custom hook remains the same as in the last post. The `App` component remains the same as well.

We are going to change the `Header` component though so that the user can change the theme:

```jsx
const Header = () => {
  const { theme, setTheme } = useTheme()!;
  return (
    <div style={{ backgroundColor: theme }}>
      <select value={theme} onChange={e => setTheme(e.currentTarget.value)}>
        <option value="white">White</option>
        <option value="lightblue">Blue</option>
        <option value="lightgreen">Green</option>
      </select>
      <span>Hello!</span>
    </div>
  );
};
```

We destructure the current theme value as well as the `setTheme` method from the `useTheme` hook. Notice that we have put an exclamation mark (`!`) after the call to the `useTheme` hook to tell the TypeScript compiler that its return value won't be `undefined`.

We have also added a drop down that has options to change the theme to _White_, _Blue_, and _Green_. The value of the drop down is set to the current theme value, and when this is changed, it calls the contexts `setTheme` method to update this in the shared state.

The full implementation of the context is available by clicking the link below. Give it a try and change the theme value and see the background change color.

[Open full implementation](https://codesandbox.io/s/react-ts-complex-context-function-f1cv4?fontsize=14&hidenavigation=1&theme=dark)

## Wrap up

Generally, we will need to create a type for a context and explicitly define the type when the context is created. Often the initial value for the context is `undefined`, so the context type is usually a union type that contains `undefined`. We need to use the non-null assertion operator (`!`) when referencing the context to tell TypeScript that it does have a value.

In the 4th post in this series, we will cover a way of not having to pass a default value to the context and not having to deal with it possibly being `undefined`. Before this, in the next post, we will learn how to consume a strongly-typed context with class components.

*Originally published at https://www.carlrippon.com/react-context-with-typescript-p2/ on Feb 25, 2020*.