React Hooks vs Redux for application state management
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:
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.
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.
npm start in the project directory to see the basic app run locally on your machine.
Similar functionality to Redux can be achieved through React Component Hooks. More specifically 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.”
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
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?
- Redux creates one large global state container whereas
useReduceris used to create a more localized shared state.
- Redux is a third-party library whereas React Hooks come out of the box with React
useContexthook gives a
Providerwhich wraps around child components to create a shared state whereas Redux provides
useSelectorhook to get the state object of interest and
actionsto manipulate the state.
- Redux dev tools allows one to time travel through the application state to see what modifications were made to the state.
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.
React Hooks vs Redux for State Management in 2021
Many developers around the world including me got confused when they heard people were using react hooks instead of…
React Hooks vs Redux Demystified
Redux and React hooks should not be seen as the opposite of each other. They are different things, with distinct goals…
Context - React
Context provides a way to pass data through the component tree without having to pass props down manually at every…