Using React ContextAPI + useReducer as a replacement of Redux as a State Management Architecture

Youry Stancato
HMH Engineering
Published in
6 min readAug 27, 2020

--

Zakintos, Greece, 2019

Introduction

With addition of a new React API called “Hooks” (since version 16.8), we’ve got a new way of manipulate the state of our application and other features in React, without the necessity of components in shape of a class.

The new method React.useState is the first and the most used “hooks” since then.

In addition to useState we have several other built in hooks that can be found in the official React documentation

But after two years since the introduction of this feature, I presume you already know how to use those methods, so today I want to go deeper and elaborate more the usage of useReducer + Context API to enable a similar state flow that Redux adds to a javascript app.

What is the design pattern behind Redux.

By description Redux has a simple design pattern: all the “write” logic of you app is converted into a single function and to execute this logic all you need is dispatch to Redux an object that describes what “action” has happened in your current state.

Then Redux applies this “action” in its state, returning a new state with the desired change.

But as I said before, you might know that already.

So I will skip any other explanation about what is Redux, flux, actions, action types, selectors, what is the meaning of life, etc…

React Context API

Since the early days of React, we know that the data sharing between components is done by top-down (parent to child). But we also know that this can generate a bunch of problems, such as bugs, verbose code, some spaghetti code that nobody wants go change. That’s how ContextAPI came to play.

Context API provides a simple way to share data between components without the need of passing props across all levels of you app tree.

https://reactjs.org/docs/context.html

How to use useReducer + Context in React.

1 — First lets create our app using the create-react-app tool

$ npx create-react-app redux-hook

2 — Now, lets navigate inside the folder that was just created

$ cd redux-hook

If you are using VScode, execute the command below to open the code in your IDE

$ code ./

3 — Now we can create our Store folder inside our App

$ mkdir ./src/store

4 — Create the Store.js file

$ touch ./src/store/Store.js

This file will be responsible for creating and sharing the Store Context within our application.

5 — Lets import inside the index.js and wrap it up our App component with the StoreProvider. We will back to this file to more implementation

6 —With previous step done, All of our components have access to our Context. Let’s do a first test adding a variable called logoColor with a value of #FF0000 (red color)

8 — We can modify the file App.js, clean it up for unnecessary code like css, logo, etc.. and import the method useStore from ./store/Store.js

If you run

yarn start

Your app will start and you should see this in your browser

9 — Cool, it’s working, now let create another small component just to test our context once more.

Don’t forget to import it in the App.js file

Our app should re-render like this:

We can see now that have a centralised way of sharing data between our components without the need of props.

We are ready to start our Redux-ish implementation using the hook useReducer and ContextAPI

10 — Let’s create our initialState: our app will be a simple pub table booking system, where the user can choose what day, what time and for how many guests they want to book a table for. In this article we will not take into account any business rules related to how many tables are available at that time, or how many two seater tables our pub supports etc…we will just save the user’s request in our state.

Booking schema

To create the tableReducer file

$ touch .src/store/bookTableReducer.js

For now we have only one array where each booking will be inserted.

We will also create the type of action that our app will perform, addBooking.

The code for our action what will be dispatched to our reducer

Let’s create our reducer with support for the add booking action.

Our bookTableReducer.js should looks like this

We can now include our initialState and reducer in our Store. So open again the Store.js and include the useReducer

We have to import initialState and reducer from our store in the index.js file and pass them as props to our StoreProvider

Hold tight soldier, we are almost there, with all this done so far, we can start creating our form component where the user will fill up with the information about the booking.

We need component folders

$ mkdir ./src/components

And for our component BookingForm.js

$ touch ./src/components/BookingForm.js

We will import our BookingForm component into App.js

Our App should look like this:

You might have noticed that we have included the useStore that comes from the state object and the dispatch method. We will now use our store’s addBooking action and dispatch our booking to our store state.

const handleOnBookATable = () => dispatch(addBooking(bookingFactory(form)));

To have visibility of this inclusion of a new booking through our form, we will create a component that lists all bookings that exist in our state bookings.

$ touch ./src/components/BookingList.js

Again we are importing useStore so that our component has access to the state of the application and anytime we execute an action in our store, our component will react and render with the new state.

We can now import our BookingList.js component into the App.js file

Congratulations, you have builded a table booking system for the local pub. You can try it out filling up the form and clicking the book a table button. The Bookinglist component will receive the new status and will render the booking item.

Now our APP has the ability to create booking through dispatch actions and the execution of reducers in our centralised state and with data sharing between our components.

Conclusion

We can conclude that, without any third party libraries, we can add a state management similar to what lib Redux provides us, thus making our APP more versatile, with smaller bundle size and future proof.

You can find the code in this repo

I would like to thank you for reading so far and if you want to get in touch please send an email to youry.stancatte@gmail.com.

Have a good hacking. ;)

--

--