DEV Community

Cover image for React Context with TypeScript: Part 1 - Simple context with function components
Carl
Carl

Posted on • Edited on • Originally published at carlrippon.com

React Context with TypeScript: Part 1 - Simple context with function components

React context allows several components in a tree to share some data. It's more convenient than passing the data via props down the component tree. How can we use TypeScript to create a strongly-typed context? Is it possible to do this for use with function components as well as class components? This is the first of four blog posts that go through this topic.

  • Part 1 - Simple context with function components (this post)
  • Part 2 - Complex context with function components (coming soon ...)
  • 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 create a strongly-typed React context and consume it in a function component that doesn't doesn't change the context.

Creating a context

A common use case for using context is to provide theme information to components in an app. We are going to provide a color value in a context that components can use.

Let's start by creating our theme using Reacts createContext function:

const defaultTheme = "white";
const ThemeContext = React.createContext(
  defaultTheme
);

We are required to provide a default value for the context, which in our case is "white".

The type of the context is inferred to be React.Context<string>:

Inferred type for a simple context

Nice - exactly as we require!

Creating a Context provider

Next, we are going to create the provider component:

type Props = {
  children: React.ReactNode
};
export const ThemeProvider = ({
  children
}: Props) => {
  const [theme, setTheme] = React.useState(
    defaultTheme
  );

  React.useEffect(() => {
    // We'd get the theme from a web API / local storage in a real app
    // We've hardcoded the theme in our example
    const currentTheme = "lightblue";
    setTheme(currentTheme);
  }, []);

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

We hold the theme value in the state. This means that when it changes, React will automatically re-render the provider's children with the new theme.

We get the current theme value using Reacts useEffect hook and update the theme state value.

Out theme provider component returns the Provider component within the context with our theme value. The provider is wrapped around all the children in the component tree.

Creating a custom hook for consuming the context

We can create a custom hook that will allow function components to consume our context:

export const useTheme = () =>
  React.useContext(ThemeContext);

Let's check what the return type of useTheme has been inferred as:

Inferred type for context hook

The return type of useTheme is string because this is the type of the context value.

Adding the provider to the component tree

The ThemeProvider component can now be placed in an appropriate position in the component tree.

const App = () => (
  <ThemeProvider>
    <Header />
  </ThemeProvider>
);

Components below it will have access to the context, but components above it won't. So, the Header component will have access to the context.

Consuming the context

The Header component can access the context by using the useTheme hook we created. This allows the header component to render an element that has its background color set to the theme color:

const Header = () => {
  const theme = useTheme();
  return (
    <div style={{ backgroundColor: theme }}>
      Hello!
    </div>
  );
};

A working version of ThemeContext is available by clicking the link below. When the app is run, Hello will appear with a light blue background.

Open working version

Wrap up

The type for the context is inferred correctly if a sensible default is provided when it is created. If the context is providing values that consumers don't change, then this is fine. However, what if we want the user to change the theme? In this case, our context would need to provide a function for updating the theme, and this can't be provided as a default value. In the next post, we will extend our theme context so that consumers can update the value.

Originally published at https://www.carlrippon.com/react-context-with-typescript-p1/ on Feb 18, 2020.

Top comments (0)