Use Hooks + Context, not React + Redux
Redux introduces a lot of complexity to our codebase with the excessive amount of code it requires. At best, this makes it an imperfect solution for state management in React applications. And yet, far too many React developers default to Redux for state management without considering other alternatives.
In this article, I will introduce the React Context API for state management, and show you what makes React Hooks plus the Context API a better solution than Redux.
Why we need a state management tool
In typical React, the way to handle data between disconnected components is through prop drilling. Since there is no global state that components can access if, for instance, you want to pass data from a top-level component to a fifth-level component, you’ll have to pass the data as a prop on each level of the tree until you get to your desired component.
This results in writing a ton of extra code, and giving components properties that they will never use also affects their architectural design. In order to solve this problem, we needed a way to provide a global state that all components, no matter how deeply nested they are, could access.
By solving this, Redux, an open-source JavaScript library for managing application state, became the go-to solution for React developers.
How Redux works
The Redux documentation describes it as a predictable state container for JavaScript applications that helps us to write applications that behave consistently, run in different environments, and are easy to test.
One disadvantage of prop drilling is the need for writing a considerable amount of extra code in order to access data from a top-level component. With Redux, this disadvantage is felt even more as a lot of complexity comes with all its extra code required for setting up a global state for our application. Redux requires three main building parts to function: actions, reducers, and store.
Actions
These are objects that are used to send data to the Redux store. They typically have two properties: a type property for describing what the action does and a payload property that contains the information that should be changed in the app state.
// action.js
const reduxAction = payload => {
return {
type: 'action description',
payload
}
};
export default reduxAction;
The type
is usually in all caps, with its words separated by underscores. For example, SIGNUP_USER
or DELETE_USER_DATA
.
Reducers
These are pure functions that implement the action behavior. They take the current application state, perform an action, and then return a new state:
const reducer = (state, action) => {
const { type, payload } = action;
switch(type){
case "action type":
return {
["action description"]: payload
};
default:
return state;
}
};
export default reducer;
Store
The store is where the application’s state is housed. There is only one store in any Redux application:
Since our application can only have one Redux store, in order to create a single root reducer for all our components, we’ll need the combineReducers
method from Redux.
With this long process and considerable amount of code required to set up Redux, imagine what our codebase will look like when we have multiple components to work with. Even though Redux solves our state management problem, it is really time-consuming to use, has a difficult learning curve, and introduces a whole new layer of complexity to our application.
Fortunately, the React Context API solves this problem. When combined with React Hooks, we have a state management solution that is less time-consuming to set up, has an easy learning curve, and requires minimal code.