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>
:
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:
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.
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)