Open In App

Why is immutability important in Redux?

Last Updated : 19 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Redux is a state container for JavaScript applications. It’s specially designed for building complex and scalable web applications. Redux is commonly used with frameworks like React and Angular for managing application state.

In this article, we will learn more about Redux and immutability in Redux.

Why Redux?

  • Redux provides a centralized store to manage the application state. That provides more consistency and data flow in complex applications.
  • As redux follows principles such as pure functions, reducers eventually come up with predictable state changes. Predictable state changes help to understand the architecture of the application and at the same time debugging the code.
  • In terms of Redux structure, it has an ecosystem with various libraries, development tools, and middleware. And as ReactJS, it has a large community support.
  • Redux is compatible with any Javascript framework such as React, Vue, and Angular. Redux is the best option for its enhancing capabilities for state management and application architecture.
  • For large applications, Redux scales well. To achieve the scalability structures state management is required. Redux maintains the performance, and application scale in size and complexity.

What is Immutability?

Immutability in Redux ensures that state objects remain unchanged once they are created. Instead of modifying state directly, Redux helps updating state by creating new copies with the desired changes. This principle ensures predictable state transitions and provides efficient change detection, debugging, and performance optimization. By using immutability, Redux increases functional programming, prevents unintended side effects, and enhances the maintainability and scalability of applications.

Why Immutability is important in Redux?

  • Performance Optimization: Immutable code structure enables more code optimization and equality checks. During the re-renders instead of deeply comparing the contents of states, it compares with references.
  • Debugging: In rudex since the state cannot be mutated, then it can easy to trace back the source of state changes more easily, it helps to identity the identity and fix bugs. Redux can also replay the state changes , that helps easier to debugging.
  • Prevents mutation side effects: Immutability in redux prevents accidental mutation side effects and help middlewares operate on consistent and predictable data.
  • Used of pure reducers: In redux reducers are the functions that create new state based on previous state and functions. To maintain the integrity of the application state, immutability ensures that reducers create a new state object without changes make in existing state.

Steps to Create the Application:

To create this react application we will use vite as faster developing experience build tool.

Step 1: Create the application using the following command

npm create vite@latest .

Step 2: Install npm using the following command.

npm install

Step 3: Install the required dependencies

npm install react-router react-router-dom react-redux tailwindcss redux-persist react-persist

Project Structure:
Screenshot-2024-04-16-173137Package.json

"dependencies": {
"bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.0.0",
"nodemon": "^3.0.1"
}

Example of Immutability in Redux with React.js application:

JavaScript
//main.jsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.jsx";
import "./index.css";
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import { persistor, store } from "./redux/store.js";
import { PersistGate } from "redux-persist/integration/react";


ReactDOM.createRoot(document.getElementById("root")).render(
    <Provider store={store}>
        <PersistGate persistor={persistor} loading={null}>
            <BrowserRouter>
                {" "}
                <App />
            </BrowserRouter>
        </PersistGate>

    </Provider>
);
JavaScript
//App.jsx

import { useState } from 'react'
import { Routes, Route } from 'react-router-dom'
import './App.css'
import Home from './pages/Home'

function App() {

    return (

        <>
            <Navbar />
            <Routes>

                <Route path="/" element={<Home />} />

            </Routes>
        </>
    )
}

export default App
JavaScript
//Home.jsx

import React from "react";
import { useDispatch, useSelector } from "react-redux";
import { decerement, increment } from "../redux/user/counterSlice";

const Home = () => {
    const dispatch = useDispatch();

    const count = useSelector((state) => state.counter.count);

    return (
        <>
            <div>
                <div
                    className="con-main"
                    style={{ textAlign: "center", marginTop: "25%" }}
                >
                    <h1>
                        Counter = <strong>{count}</strong>
                    </h1>
                    <div
                        className="con1"
                        style={{ display: "flex", justifyContent: "space-evenly" }}
                    >
                        <button
                            style={{
                                border: "1px solid black",
                                borderRadius: "2px 2px 3px 2px",
                                padding: "0.5rem 1rem",
                                backgroundColor: "green",
                            }}
                            onClick={() => dispatch(increment())}
                        >
                            Increment
                        </button>
                        <button
                            style={{
                                border: "1px solid black",
                                borderRadius: "2px 2px 3px 2px",
                                padding: "0.5rem 1rem",
                                backgroundColor: "red",
                            }}
                            onClick={() => dispatch(decerement())}
                        >
                            Decrement
                        </button>
                    </div>
                </div>
            </div>
        </>
    );
};

export default Home;
JavaScript
//store.js

import {
    combineReducers,
    configureStore,
    getDefaultMiddleware,
} from "@reduxjs/toolkit";
import userReducer from "./user/userSlice.js";
import { persistReducer, persistStore } from "redux-persist";
import storage from "redux-persist/lib/storage";
import counterReducer from "./user/counterSlice.js";

const rootReducer = combineReducers({
    user: userReducer,
    counter: counterReducer,
});

const persistConfig = {
    key: "root",
    version: 1,
    storage,
};

const persistedReducer = persistReducer(persistConfig, rootReducer);

export const store = configureStore({
    reducer: persistedReducer,
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
            serializableCheck: false,
        }),
});

export const persistor = persistStore(store);
JavaScript
//counterSlice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
    count: 0,
}

const counterSlice = createSlice({
    name: 'counter',
    initialState,
    reducers: {
        increment: (state) => {
            state.count += 1;
        },
        decerement: (state) => {
            state.count -= 1;
        }
    }
})

export const { increment, decerement } = counterSlice.actions

export default counterSlice.reducer


Immutability Demonstrated:
Now for the above in the increment and decrement state we directly change the count value by +1 and -1 that violates immutability rules. So instead of directly modifying the count value we can return a object. Here in this example we just updated the new state object with count value.

JavaScript
//counterSlice.js

import { createSlice } from "@reduxjs/toolkit";

const initialState = {
    count: 0,
}

const counterSlice = createSlice({
    name: 'counter',
    initialState,
    reducers: {
        increment(state) {
            return {
                ...state,
                count: state.count + 1
            };
        },
        decrement(state) {
            return {
                ...state,
                count: state.count - 1
            };
        }
    }
})

export const { increment, decerement } = counterSlice.actions

export default counterSlice.reducer

To start the application run the following command

npm run dev

Output:

Redux Immutability example

Redux immutability example



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads