DEV Community

Cover image for Add Redux to your React app in 6 Simple Steps
Thisura Thenuka
Thisura Thenuka

Posted on • Updated on • Originally published at simplecoder.hashnode.dev

Add Redux to your React app in 6 Simple Steps

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.

Redux Store

what-say-you

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";
Enter fullscreen mode Exit fullscreen mode

An action is any object with a type property. Actions help us describe what needs to happen.

{
    type: ADD_TASK
}
Enter fullscreen mode Exit fullscreen mode

An action creator is simply a JS function that returns an action.

export const addTask = (task) => {
    return {
        type: ADD_TASK,
        payload: task,
    };
};
Enter fullscreen mode Exit fullscreen mode

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.

redux actions

Your app can change the state in the Redux store by dispatching actions.

dispatch(addTask(task))
Enter fullscreen mode Exit fullscreen mode

how-is-that-possible

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;
  }
};
Enter fullscreen mode Exit fullscreen mode

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.

Redux Cycle

Feeling Overwhelmed?

complex

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.

To-Do App

Step 1 - Installing required libraries

npm i redux react-redux @reduxjs/toolkit
Enter fullscreen mode Exit fullscreen mode

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";
Enter fullscreen mode Exit fullscreen mode

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,
    };
};

Enter fullscreen mode Exit fullscreen mode

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;

Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Step 4 - Create the Store

import { configureStore } from "@reduxjs/toolkit";
import rootReducer from "./rootReducer";

const store = configureStore({ reducer: rootReducer });

export default store;
Enter fullscreen mode Exit fullscreen mode

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>
);
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

That's about it. We successfully added Redux to a React app.

dance

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)

Collapse
 
brense profile image
Rense Bakker

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.

Collapse
 
lexiebkm profile image
Alexander B.K.

"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.

Collapse
 
brense profile image
Rense Bakker

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.

Collapse
 
thisurathenuka profile image
Thisura Thenuka • Edited

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.

Collapse
 
brense profile image
Rense Bakker

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.

Collapse
 
lexpeee profile image
Elex

Usually for smaller applications, ContextAPI is way better imo.

Collapse
 
thisurathenuka profile image
Thisura Thenuka

True. Way less boilerplate code. Easier to implement ❤️

Collapse
 
ash55 profile image
delirent

Ever since I learned RTK, I don’t write redux anymore.

Collapse
 
clay profile image
Clay Ferguson

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.

Collapse
 
thisurathenuka profile image
Thisura Thenuka

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 ❤️

Collapse
 
clay profile image
Clay Ferguson

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! :)

Collapse
 
danjelo profile image
danjelo • Edited

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".

Thread Thread
 
clay profile image
Clay Ferguson

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.

Thread Thread
 
danjelo profile image
danjelo

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.

Thread Thread
 
clay profile image
Clay Ferguson

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.

Collapse
 
prototypik profile image
Thomas Shallenberger

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.

Collapse
 
clay profile image
Clay Ferguson

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.

Thread Thread
 
danjelo profile image
danjelo • Edited

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!

So I'm still skeptical about those who say redux is still needed.

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/....

Thread Thread
 
clay profile image
Clay Ferguson

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/...

Collapse
 
elibates profile image
Eli Bates

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/

Collapse
 
brense profile image
Rense Bakker

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.

Collapse
 
thisurathenuka profile image
Thisura Thenuka • Edited

Looks interesting. I'll definitely check it out. Thanks for sharing ❤️

Collapse
 
jesusantguerrero profile image
Jesus Guerrero

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.

Collapse
 
lexiebkm profile image
Alexander B.K.

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.

Collapse
 
thisurathenuka profile image
Thisura Thenuka

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.

Collapse
 
zeeshanali0704 profile image
ZeeshanAli-0704

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

Collapse
 
thisurathenuka profile image
Thisura Thenuka • Edited

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...

Collapse
 
zeeshanali0704 profile image
ZeeshanAli-0704

Thanks!