React Hooks vs Redux for application state management

Ankit Trehan
6 min readOct 12, 2021

When you wound want to use Hooks and/or Redux

Managing state in the traditional way in a complex react application can get very messy very quickly. Passing state objects down to the grandchildren of an object to allow updates from the children can not only become extremely cumbersome but also create unintended errors.

Take an example of an e-commerce application, a typical page might be made of smaller components like the top nav bar, a search bar, a list of products etc. These components might be made of even smaller components like a nav item, or a price component, an image component, a title component, a buy now button component, etc for the items on sale. When a user adds an item to their cart, the simple action might fire multiple state updates, like updating the user’s cart, showing new recommended products to the user, and even updating the availability of the product etc. So many updates in multiple components at different levels can lead to an app that is difficult to manage and debug.

The solution? Creating a shared state between all relevant components. Two of the approaches to manage a shared state might be using React component hooks or Redux. We will create a boilerplate redux app that comes with create-react-app and try to recreate it with Hooks. So let’s dive into how Redux and Hooks compare while trying to manage shared states:

1. Redux

What is redux?

Redux is a predictable state manager for JS containers. In simple words, it can help you manage global state in which state is mutated based on events which are called actions. The redux library also comes with a bunch of tools which help with the development experience and reduce some complexities associated with setting up redux.

So how do I know when to use Redux?

According to Redux’s official documentation, “Redux is most useful when in cases when:

  • You have large amounts of application state that are needed in many places in the app
  • The app state is updated frequently
  • The logic to update that state may be complex
  • The app has a medium or large-sized codebase, and might be worked on by many people
  • You need to see how that state is being updated over time”

In other words, use redux when you have a large complex application with intricate updates to the state while having multiple components manipulating it.

Setting up a sample application for Redux is quite simple:

npx create-react-app my-app — template redux

Diving into detail about the code:

The boilerplate redux (sample app) created imports Counter in the main App.js file which is a feature for our redux application. The Counter component uses dispatch actions imported from CounterSlice and also uses Hooks to access/manipulate the state.

A slice which we saw above can simply be defined as a collection of redux reducer logic and actions for a single feature in your app. We can combine such slices to create a root reducer which redux expects. This is seen in the src/app/store.js file. The file creates a store and defines a root reducer object that contains Reducer slices.

The counterSlice file when creates reducer functions that are imported in the Counter component and are used along with the useDispatch hook from redux to manipulate the state on button clicks.

This boilerplate app gives us an idea of how redux is set up and how to build tinier reducer slices while using them to have one bigger root reducer function.

Finally, run npm start in the project directory to see the basic app run locally on your machine.

2. Hooks

Similar functionality to Redux can be achieved through React Component Hooks. More specifically the useContext and useReducer hooks.

The useContext hook provides a way to avoid passing down state as props between parent and children. From the official documentation, “Context provides a way to share values like these between components without having to explicitly pass a prop through every level of the tree.”

The useReducer helps to call a reducer function which it accepts as its first argument and takes an initial state as its second argument. The reducer function itself takes in a state and an action which is used to manipulate the state of an object. useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.

Sounds cool, how do you implement state management with hooks?

Create a new react project with the following line:

npx create-react-app hooks

After creating the project replace code in App.js with the following:

Let’s break this down and see what is happening here (Github repo):

We start by wrapping our content inside the Appfunction with the ExamplePovider component. For the ExampleProvider component we first define the ContentExample context instance with React.createContext() . We use this to create our provider as well as pass it in as a parameter to the useContext hook (more on it later).

Inside the ExampleProvider component we first define a reducer that takes in a state and action (think of them as events to mutate the state). The action parameter usually defines two properties: type which defines the type of mutation to take place and payload which is used to pass in data to the reducer functions.

Inside each of those reducer functions based on action.type we define return statements. We use the spread operator like ...state so as to update values and not add/delete things from state. Finally we use a useReducer hook to pass in our reducer and our initialState which gives us a state and a dispatch function. This array is then passed as a value into the ContentExample.Provider tag which gives us our provider which takes in a children component that it provides the values to.

Finally in the components that are children of the provider, we use the useContext hook to extract the state and dispatch function that we passed in earlier to the Provider. Using the dispatch function we can now dispatch different actions to manipulate the global state.

So, what does this look like? Run npm start to bring up the application

3. Comparisons

The above two apps created are extremely simple and give an idea of how both Hooks and Redux are implemented. In a real-world complex application, the process of using both will certainly be more complex.

After going through both of the options to create a shared state, now we come to the million dollar question: How are they different?

Differences:

  • Redux creates one large global state container whereas useReducer is used to create a more localized shared state.
  • Redux is a third-party library whereas React Hooks come out of the box with React
  • useContext hook gives a Provider which wraps around child components to create a shared state whereas Redux provides useSelector hook to get the state object of interest and actions to manipulate the state.
  • Redux dev tools allows one to time travel through the application state to see what modifications were made to the state.

Tl:Dr; Summary

So all in all, Redux and React hooks should be regarded as tools in your arsenal to be used together and not something that you need to pick between. For smaller projects, the clear winner are React Hooks for ease of use whereas for more more larger, complex applications Redux and Hooks can be used in conjunction. Redux should be preferred to set up global state for applications while React Hooks should be used for managing the local state within components.

References/Further Reading:

--

--