Redux is a predictable state container for JavaScript apps.
Do I need to use Redux in my React app?
It depends.
If your app is simple and works with a minimal number of states, there is no need to add redux to your app.
If your app is complex and has a lot of stuff going on, you should consider adding redux to your app. It will provide a clean and simple structure to handle states and improve the longevity of the code.
Redux vs useContext and useReducer
By utilizing React's useContext and useReducer hooks, we can provide the same functionality as Redux. The boilerplate code is significantly less, and it is much simpler.
But it's crucial that you understand how Redux functions because most software companies already use it in their projects, and it will undoubtedly help you in your next interview.
Redux Basic Concepts
There are a few simple concepts you should understand before adding redux to your react app.
Store
The Redux Store holds the state of your app. Your app is always subscribed to the store. But your app can't directly change the state in the store.
This is where actions and action creators come in.
Action Types, Actions and Action Creators
Action types are constants that are used to define the values used for the type property of Actions.
export const ADD_TASK = "ADD_TASK";
An action is any object with a type property. Actions help us describe what needs to happen.
{
type: ADD_TASK
}
An action creator is simply a JS function that returns an action.
export const addTask = (task) => {
return {
type: ADD_TASK,
payload: task,
};
};
The only thing your app can do is to "dispatch" actions. Dispatch is a method provided by the react-redux library's useDispatch
hook. I'll get into this later when we implement the code.
Your app can change the state in the Redux store by dispatching actions.
dispatch(addTask(task))
An action only describes what needs to happen. It doesn't describe how we want the states to change. We need to use a reducer for this.
Reducer
A reducer is a JS function that takes in initialState
and action
as input and returns an updated state object.
const initialState = {
tasks: [],
taskTitle: "",
taskDescription: ""
};
const taskReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TASK:
return {
...state,
tasks: [...state.tasks, action.payload],
taskTitle: "",
taskDescription: ""
};
default:
return state;
}
};
When an action is dispatched, the relevant reducer will update the state according to the action.
When we create the Redux store, we feed all the reducers to the store. So, the reducers can update the state in the store which is what we want.
Feeling Overwhelmed?
I completely understand if you feel swamped, especially if you are only now becoming familiar with these ideas. Have no fear; I've been there. When we begin writing the code, you will have a better understanding of it.
Adding Redux to your React App
Redux will be used to manage states in my simple to-do app, which will allow users to add tasks. I'm simply doing this as a demonstration; a basic app like this doesn't need redux.
Step 1 - Installing required libraries
npm i redux react-redux @reduxjs/toolkit
Step 2 - Create Actions, Action Types and Action Creators
taskActionTypes.js
export const ADD_TASK = "ADD_TASK";
export const UPDATE_TASK_TITLE = "UPDATE_TASK_TITLE";
export const UPDATE_TASK_DESCRIPTION = "UPDATE_TASK_DESCRIPTION";
taskActions.js
import {ADD_TASK, UPDATE_TASK_DESCRIPTION, UPDATE_TASK_TITLE} from "./taskActionTypes";
export const addTask = (task) => {
return {
type: ADD_TASK,
payload: task,
};
};
export const updateTaskTitle = (value) => {
return {
type: UPDATE_TASK_TITLE,
payload: value,
};
};
export const updateTaskDescription = (value) => {
return {
type: UPDATE_TASK_DESCRIPTION,
payload: value,
};
};
When we are passing values to action creators, the convention is to call that attribute "payload".
Step 3 - Create Reducer(s)
taskReducer.js
import {ADD_TASK, UPDATE_TASK_DESCRIPTION, UPDATE_TASK_TITLE} from "./taskActionTypes";
const initialState = {
tasks: [],
taskTitle: "",
taskDescription: ""
};
const taskReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TASK:
return {
...state,
tasks: [...state.tasks, action.payload],
taskTitle: "",
taskDescription: ""
};
case UPDATE_TASK_TITLE:
return {
...state,
taskTitle: action.payload,
}
case UPDATE_TASK_DESCRIPTION:
return {
...state,
taskDescription: action.payload,
}
default:
return state;
}
};
export default taskReducer;
In this case, we only need the taskReducer. But in a complex app, there will be multiple reducers to handle different states. In that case, the convention is to create a reducer called "rootReducer" by combining all the other reducers and feeding that reducer to the store.
rootReducer.js
import {combineReducers} from "redux";
import taskReducer from "./tasks/taskReducer";
const rootReducer = combineReducers({task: taskReducer});
export default rootReducer
Step 4 - Create the Store
import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./rootReducer";
const store = configureStore({ reducer: rootReducer });
export default store;
Step 5 - Wrap the Root Component with Provider
index.js
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import {Provider} from "react-redux";
import store from "./redux/store";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
Step 6 - useDispatch() and useSelector()
You can use useSelector() to access the states in the Redux store.
You can use useDispatch() to access the dispatch method which helps you to dispatch actions
import React from "react";
import {useDispatch, useSelector} from "react-redux";
import {addTask, updateTaskDescription, updateTaskTitle} from "../redux/tasks/taskActions";
const styles = {
taskContainer: {
display: "flex",
flexDirection: "column",
width: "350px"
},
mainContainer: {
textAlign: '-webkit-center'
}
}
function AddTasks() {
const dispatch = useDispatch();
const taskTitle = useSelector(state => state.task.taskTitle)
const taskDescription = useSelector(state => state.task.taskDescription)
const onAddTask = () => {
const task = {
title: taskTitle,
description: taskDescription,
}
dispatch(addTask(task))
};
const onTaskTitleChange = (e) => dispatch(updateTaskTitle(e.target.value))
const onTaskDescriptionChange = (e) => dispatch(updateTaskDescription(e.target.value))
return (
<div style={styles.mainContainer}>
<div style={styles.taskContainer}>
<input type="text" placeholder="Task Title" onChange={onTaskTitleChange} value={taskTitle} />
<input type="text" placeholder="Task Description" onChange={onTaskDescriptionChange}
value={taskDescription} />
<button onClick={onAddTask}>Add Task</button>
</div>
</div>
);
}
export default AddTasks;
That's about it. We successfully added Redux to a React app.
If you want to look into the code and try in out for yourself, here is the Code Sandbox version.
https://codesandbox.io/embed/divine-platform-43jj6l?fontsize=14&hidenavigation=1&theme=dark
Conclusion
At first glance, adding Redux to your React app could appear difficult. But it gets a lot simpler when we take each concept in turn and develop the logic on our own.
I am still trying to learn these concepts on my own. Please feel free to bring up any gaps in my reasoning and the problems you encounter.
Thank you for reading my article. If you learned something new, please make sure to drop a like and share the article among your fellow developers 🥳
Top comments (28)
My problem with Redux is not that it's too difficult, it just adds way too much boilerplate to do a simple thing. If you want to make a change, you have to touch 4 different files, often spread all over the project (the state model, the action, the reducer and the selector). Redux does not follow the "keep related things together" principle of clean coding. With Redux Toolkit the overhead is greatly reduced so thats good, however I still wish people would stop for a second and think about what problem they're actually trying to solve with Redux and do a little bit of research on what state management is and what different solutions are out there.
You're right though, Redux appearantly is the de facto standard for state management in React enterprise applications, so using it guarantees some form of "longevity" in the sense that there will probably be people who know how to maintain the code for some time... However the fact that Redux is promoting Redux Toolkit now, which is a pretty big break from the traditional way of Redux is quite revealing and I suspect Redux won't be the industry standard in 10 years, considering the low adoption rate of Redux Toolkit. Other state management solutions will slowly gain more traction as people realize that other state management solutions allow them to develop code much faster and cleaner.
Hi Rense, Thank you so much for joining in for the discussion and providing your valuable input.
My main motivation to learn Redux was that majority of companies have existing projects that works with Redux. So, it would always be a plus point at an interview.
But then again, you're absolutely right ❤️. We need add a lot of boilerplate code to do simplest of things.
True, learning Redux is probably useful for the time being, it's what a lot of companies use and like you said, it can be a plus during interviews.
"considering the low adoption rate of Redux Toolkit"
Do you mean people are still reluctant to use RTK, despite the benefits we can have from using it ? Probably some people just don't know there is RTK, so they stick to core Redux with its inherent boilerplate.
I think it's both, some dont know it exists and some know it exists, but they refuse to use it because its different from what they're used to.
Usually for smaller applications, ContextAPI is way better imo.
True. Way less boilerplate code. Easier to implement ❤️
Ever since I learned RTK, I don’t write redux anymore.
I just removed Redux completely from my app yesterday, and replaced it with what's shown here:
towardsdev.com/stop-using-redux-yo...
TL;DR, React is currently capable of doing what Redux does, so there's no need for the extra dependency, and you can simplify your code by removing redux.
You're correct. useContext + useReducer provides way less boilerplate code for the same functionality. I'll update the article accordingly. Thank you for the valuable comment ❤️
Your post will still be very valuable to Redux users. Not everyone can switch to useContext+useReducer right away,
Also it was tricky getting it to work, at first, because with UC+UR approach you have to make the app wait until the first render of your root component before you can manage state (dispatch actions), because you can't 'initialize it' until you're already inside a render function where it's possible to call a hook without breaking the "Rules of Hooks".
Anyway your post was very good and I should have said so too! :)
A bit of caution though when replacing Redux with useContext + useReducer. Performance might suffer a lot depending on the application and re-render scenario.
Anyway great article.
I have found that Redux really shines when updating different components connected to the same state and they are part of a large nested component hierarchy with many unrelated components that should not re-render .
And so powerful if a component itself is connected to a slice of state then it could be arbitrarily moved within the entire component hierarchy.
Also I see Redux as a sort of separation of "what happened" (event) and "how to update any state based on that event".
I'm seeing the UC+UR approach running at east as fast as with Redux. Redux doesn't have anything to do with the rendering efficiency, afaik. All that is done by the core React framework. If anything it seems like this approach is faster.
I have potentially one of the most DOM heavy (1000s of DOM elements per page) apps in the world (quanta.wiki) and it's lightning fast. But I do appreciate the warning.
I was a bit unclear, you are correct, Redux itself has nothing to do with rendering or react. React-redux has though and is what is passing updates to react in a very optimized manner.
Anyways, I have read a few articles (not trying myself) that Context has performance limitations since any component that consumes context will be forced to re-render and you might need workarounds like useMemo etc.
I don't mind if my render functions get called a lot, because even those have nearly zero impact on performance. What hurts performance is the TRUE DOM modifications when they happen.
So, for example, if I have 1000s of components "consuming context" and I make some global state change that only affects the ACTUAL DOM for one item, then only that one item will consume non-negligible CPU (i.e. DOM update). That's what makes React so fast. It only updates the DOM for things that DID change. So I'm still skeptical about those who say redux is still needed. They may have a bias.
Incorrect. useContext forces rerender on any change within the context variable. Redux uses selectors to allow components to subscribe to small slices of state change.
You should not interchange useContext and Redux.
I have an app with VERY many DOM elements, and when I do a global state change, React is smart enough to only update the part of the DOM that actually changed, which is why it's lightning fast. You can count rerenders if you want, but those affect performance one millionth as much as DOM changes...which is why my app has the same exact performance without Redux as it did with Redux...and if anything is faster.
Yeah React is considered fast, still there are scenarios where it is not fast enough by itself or in combination with things like Context. One example is updates/re-renders that occurs when typing , animations, scrolling etc. where you might have to resort to things like Pure functions or such. Also, update the DOM is one thing, expensive calculations might also be part of a component.
Sounds like you found the perfect solution for your app though!
It was some time ago I worked with theese things, so there might be better alternatives but this is an excellent writeup as of Redux/Context:
blog.isquaredsoftware.com/2021/01/....
There are plenty of ways to shoot yourself in the foot, but if anything compute-intensive is being done in a render method that's a mistake on the developer's part, and a violation of MVC principles too.
Also my app doesn't use hardly any JSX/TXS either. I have a framework of components that calls 'createElement' directly, but all that is abstracted away as you can see by looking at how my LoginDialog renders GUI...
github.com/Clay-Ferguson/quantizr/...
Lately I’ve been preferring Jotai and Zustand over context and redux. The thing I don’t like about context is that you basically are forced to share it with components you don’t want to share it with. Jotai has a cool atom based approach and is worth looking at.
jotai.org/
zustand-demo.pmnd.rs/
Jotai is great, I've been trying to push for it to be used more in enterprise applications, so far no luck though :( but i'll definitely keep trying.
Looks interesting. I'll definitely check it out. Thanks for sharing ❤️
Hey Matt,
Well tbh, I didn't know about Redux Tool kit's full functionality when I was writing the article. I was initially going for the old-fashioned redux style. When I was configuring the store, the
createStore()
method was shown as deprecated and hence I used the recommended imports(RTK).I'm still trying to learn these concepts. I'll definitely update the article accordingly when I get a hang of the RTK. Sorry for the inconvenience, if there was any.
Good write up, thanks for sharing.
Redux might seen intimidating at first or just too much for a data store but as a data time machine is a great tool to get was going on in a big project.
Also in RTK we can use create slice and they return the actions and reducer.
Although having a good grasp on Redux core is important, the Redux team strongly recommend using Redux Toolkit (RTK) to ease our work in using Redux, as mentioned many times in the official doc. I am still learning, though, but surely I will follow the recommendation, including RTK Query for dealing with data fetching and caching.
At times we need to configure store for production env & at times for dev env - how to do that!
Use case: Like i want to see my dev tools only for development environment & not production env
Hi Zeeshan,
I haven't actually done it myself. I'll share my experience when I do. But for the time being, I believe the following links would definitely help you out
stackoverflow.com/questions/609094...
medium.com/@zalmoxis/using-redux-d...
Thanks!