How to use the react context API with asynchronous reducer

Patrick Groß
3 min readMay 12, 2019

--

Photo by Burst on Unsplash

Using a reducer function with react context is common practice, but what if we want to use asynchronous functions inside your reducer, to for example call an remote API and then making a state update?

In this example we will build the famous counter with the react context API and asynchronously update our counter state.

I will assume you have a basic understanding of react, react hooks and the context API.

Edit: I added a payload example to the github repository, check it out if you’re interested.

Implementing the context

src/
|_ components/
| |_ Counter.js
|_ context/
| |_ CounterContext/
| |_ createActions.js
| |_ index.js
| |_ reducer.js
|_ hooks/
| |_ useAsyncReducer.js
|_ App.js
|...

I like to put my context in its own folder and split the context/provider, actions and reducer in separate files.

The context and provider

In the index.js we export the CounterProvider and the CounterContext. The CounterProvider is nothing special, but instead of the useReducer hook it uses the useAsyncReducer hook we will implement later.

As a side note, the reason we set the provider value to an array instead of an object, is so that if we have multiple contexts we can easily destructure an array with a custom variable name, like we do with useState.

The action creator

The counter has three actions: up, down and reset. We just take the given dispatch function and call it with the corresponding action type.

The dispatch function will return a Promise, so we also want to return it in our actions.

The reducer function

Since we don’t really have a remote API to call, we wrap our state logic inside a Promise and resolve it with a setTimeout to simulate an asynchronous call.

All that is missing now is the asynchronous reducer hook.

Asynchronous reducer hook

Now we finally implement our asynchronous reducer hook. Just like the normal useReducer we have the reducer function and the initial state as parameters and return the state and a dispatch function inside an array.

The magic happens in our custom dispatch function which expects the reducer to return a promise and when this promise either resolves or catches an error it will update the state accordingly.

In line 8 we just make sure that the reducer actually returns a promise or we just update the state the normal way.

Keep in mind since we marked the dispatch function as async (line 6) it will always return a promise, even if the reducer doesn’t return a promise.

Finishing up

Now we only need to create the counter component.

In line 7 we get the counter context with the useContext hook and use array destructuring to name our counter state.

With the loading state (line 8) we prevent the buttons to be clicked while the asynchronous call is still being executed.

In the handleAction function we set the loading state for our actions accordingly. Since our actions return the promise from our asynchronous dispatch function, we can simply use the finally callback to revert our loading state.

Final result

Thats it, thanks for making it this far and I hope you learned something.

Since this is my first medium article I would love your feedback.

--

--