React Context API with hooks and TypeScript

JavaScript React TypeScript

With React, we have quite a few options when it comes to state management. One of the most obvious ones is using the local state. Unfortunately, it might lead to prop drilling. It happens when we pass data from one component through many layers. When some of the components along the way do not need the data but only help passing it around, it starts to feel like an issue.

Due to the above issue, the React community implemented various solutions. Redux is one of the most popular, and the Context API didn’t replace it. In fact, Redux uses context under the hood. In this article, we learn what the context is and how to use it with TypeScript and hooks.

Introducing the context

The React Context allows us to provide data through components. With it, we don’t need to pass them down manually through every level of components. We can also update the data from any child component. Doing so affects all the other places that use the same context.

Creating the context

The first step in creating our context is calling the   method.

The next step is to provide the above context to our application. Every context that we create has a Provider that we need to use. Thanks to doing so, our components can subscribe to the context changes.

You might notice above that we provide the default value in two different places. When there is no Provider, React uses the value provided to the   function. It might come in handy when testing components without wrapping them with a Provider. We expand more on this concept further below.

Consuming the context

To consume the above context, we can use the   hook. We need to give it the context object created above. In return, we get the current context value.

We could also do the above with the use of 

Under the hood, the   looks for the nearest   and gives us its value.

Using state in our context

One of the most common use-cases with the context is a situation in which we need to update it. To do so, we need to keep our values in an instance of a local state. To do so, we can use the   hook. At first glance, it might look like a good idea to put this logic in our parent component.

The above is not the most graceful approach, unfortunately. Instead, I suggest creating a custom hook using the facade design pattern.

If you want to know more about the facade pattern, check out JavaScript design patterns #3. The Facade pattern and applying it to React Hooks

Now, we can use the above hook to clean up our  component.

Updating the context within the child components

Thanks to doing all of the above, we can start updating our context from child components. First, let’s write a hook for loading our posts.

Now, we can use that in our   component.

Further improvements

One of the most straightforward ways to improve the above code is not to use the arrow function in our  template. Doing so causes our arrow function to be recreated on each render, which can hurt our performance. Let’s create a separate hook for dealing with this handler.

Now, our   function returns another function. Thanks to that, by calling   we create a removal handler for a particular post.

Skipping the default value for the createContext function

Another interesting adjustment to our code is the one suggested by Kent C. Dodds in his article. Although providing the default value for the   function allows us to use the context without the provider, it might not be the best approach. Even though the documentation states that it can be useful for testing, we might prefer our tests to be more similar to the way our application works.

To skip the default values for the   function, we need to adjust our typing slightly.

Instead of calling   every time, we use the   hook, as suggested by Kent C. Dodds.

Memoizing the context value

We can use the   hook to improve the performance of our application and get rid of some of the unnecessary rerenders. Let’s look into the official documentation:

All consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes.

To deal with this, let’s use the   hook in our  :

Summary

In this article, we’ve looked into how we can use React Context API with TypeScript. While it does not replace Redux, it still has use-cases. We’ve also dealt with a few issues, such as improving the performance by dealing with unnecessary arrow functions and memoizing the context value. To avoid providing the default value multiple times, we’ve created our custom hook for getting the current value of the context.

What do you think about the solution provided above? Would you implement some additional improvements?

Subscribe
Notify of
guest
6 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Grzesiek
Grzesiek
3 years ago

In section Further improvements theres a mistake.

const PostsList = () => {
const { posts, handlePostRemove } = usePostsListManagement();
usePostsLoading();
return (
<div>
{
posts.map(post => (
<div key={post.id} onClick={handlePostRemove(post.id)}>
<h2>{post.title}</h2>
<p>{post.body}</p>
</div>
))
}
</div>
)
}

In that line
<div key={post.id} onClick={handlePostRemove(post.id)}>

You actually execute handlePostRemove not on click, but at render. what You are interested in is most likely
const postId = post.id;
const handleClick = useCallback(() => handlePostRemove(postId), [handlePostRemove, postId])

.
<div key={post.id} onClick={handleClick}>

Dmitriy
Dmitriy
3 years ago
Reply to  Grzesiek

He used curry function, so handlePostRemove has already returned callback

Prabhu
Prabhu
3 years ago

This is the one im looking for. So clear . Thanks a lot🙏 👍

Tero
Tero
3 years ago

Thank you, finally example that worked as intended!

Reberth
2 years ago

my mind blows

Ofer Gal
Ofer Gal
2 years ago

Good post and explanation.
I am trying do add “Posts” by user action (in my case the “Posts” are uploaded files)
so running the fetch in .useEffect(… is not an option
how can I add Posts at the function App() in a function?
Should I go back to the step before “Now, we can use the above hook to clean up our App component.” ?
Or should I add the code to add posts in the “PostsList” ?