George Iliadis

React State, Redux or Hooks?

Disclaimer: this is not a post intented to teach you Redux or Context.

Recently whilst browsing the reactjs subreddit I came across a thread of someone having trouble understanding Redux. A user commented below, that once hooks came around he ditched redux. And to my surprise he got downvoted and the most upvoted comment on his thread was, and i'm paraphrasing "What do hooks have in common with Redux, they solve complete different problems." Well, the reality is that redux can be replaced by a combination of useContext and useReducer. And especially given the current state of React and its upcomming features. Since redux won't seem to be compatible with the concurrent-mode that is coming. But that is a different topic.. back to the argument of replacing redux with hooks. My position is that 100% you can completely replace Redux, with a combination of useContext and useReducer. And in fact I made such a code example that we will get to look at and compare.

But first lets see what Context and Redux do and their approach to things.

REDUX

React-Redux per its website description is a predictable, encapsulated and optimised global state management library. It works by creating a global store, where you save data that you might want to access to multiple levels of your component tree with out passing them down from parent component to child component all the way. It helps keep your code base clean andn top of all of it, it works with reducer functions and actions. To simplify the idea behind their use, instead of having a huge object with data that you pass down a tree, you can trigger "actions" with essentially return the bare minimum data required. Helping with performance.

Redux is great, it is awesome and it works.

CONTEXT & HOOKS

Before i get to the hooks part, lets begin with Context API, context came to the React library to do what Redux does as a now native feature of React itself. You see, prior to that, if you wanted global state management you absolutely HAD to use Redux or another library. React came with no such feature, up until the Context API rolled live. And it was good, it provided a global state so you could save your data and have it accessible in whatever depth of your component tree you wanted.

Then hooks rolled over, and things like useContext and useReducer came about. The useContext hook gave us the same functionality as above with with a lot of cleaner code. And if used in combination with the useReducer hook you could have access exactly the little piece of state you wanted with an added layer of security of having the state behave predictably due to the reducers being pure functions.

A little note about pure functions, without going into great detail, pure functions are functions that always given the same input will always return the same output.

A good example, a function that gets a and b and returns the sum of them, if given the values 1, 5 will always return 6 that is a pure function.

A bad example, a function that gets a and returns the current time + a hours is not pure. Because the current time will never be same, and depending on when you run it, it will return a different output each time.

On top of that reducers add the extra security layer of not allowing you to mutate state dirrectly.

Such an example is this

stateObj = { user: "John", isLogged: false }

under no circumstance you are allowed to do this inside a pure function.

stateObj.isLogged = true;

LETS SEE SOME CODE

Ok now that we got the theory out of the way lets get into a practical example. I bootstraped a repo with create-react-app that is a simple cart. You have 2 directories. '/' (Home.js) and '/cart' (Cart.js).

On Home you get to add products to your cart, each click adds the product to your cart and updates it, if does not exist it adds it, if it does it will update the amount by increamenting it by 1.

Then you can go on your cart, look at what you have inside of it, and remove items from it. It is a very simplistic approach on a very common use case.

REPOSITORY

Link to the repository: -> REPO <-

On the repo we have 3 branches.

master: This branch solves the issue without using Redux or Context & Hooks, you have your cart state in you App component, you pass down a method to update it on the Home component and you pass the value of the cart down to your Cart component with a method to remove items from it.

redux-version: This branch is the redux methodology applied, I am using the latest redux version with its own hooks and a combine reducer method.

hooks-version: This branch is the implementation of a global state with useContext and useReducer.

REDUX VERSION

Here we have a global store, that holds the cart state.

REDUX 1- global store

Inside the allReducer (combine Reducer) we have a productReducer that is the reducer for our cart.

REDUX 2-combine reducer

That reducer has 2 actions, ADDPRODUCT and REMOVEPRODUCT.

REDUX 3- reducer

We have our action functions that get our payload.

REDUX 4-actions

And to component we need to make use of that global state we dispatch actions with the useDispatch redux hook.

REDUX 5-dispatching actions

HOOKS VERSION

Now lets see how we would go about the same exact thing as above with the use of Hooks.

We have our Globalstate, which we use to wrap our CartContext around our Component tree.

HOOKS 1 global state

Inside there we declare our state with useReducer.

HOOKS 2 global state component and state

Now useReducer is declared as such.

You need your state object, which in our case is cart, A dispatch method, you can call it whatever, I for simplicity called it dispatch. Your reducer method, which is the cartReducer. An initial state which is the empty array.

HOOKS 3 dispatch

Then we need our actions, which we declard as methods inside our action folder.

HOOKS 4 actions

And then we declare our reducer on its own file which is essentially the same exact reducer as before.

HOOKS 5 reducer

And we pass them down as values on our Provider.

Then in the component needed, we load and consume the Context we want (we can have more than one context), destructure and get only the methods and data we want, and trigger them.

HOOKS 6 consume context

And thats it, same exact functionality, with a different approach having all the benefits or predictability and effectiveness of Redux.

CONCLUSION

Now, i am not gonna say if one method is better than the other. I was just trying to answer weather or not Redux can be replaced by Hooks. Weather or not more people will transition to using hooks remains to be seen especially given the newest updates with lazy loading and concurrent mode that are coming.

Thanks for the read! If you want to contact me and get a discussion going, feel free to do on my LinkedIn or Github.

Date: