Search

Redux Mastery: A Practical Guide Through a Mini-Project

post-title

Introduction
Redux is a powerful library for managing the state of your JavaScript applications. It follows a unidirectional data flow pattern, making it easier to manage the state, especially in complex or multi-panel applications. 
Here, we'll provide a beginner-friendly explanation and a mini-project example to help you understand Redux better.

What is Redux?
Redux is an open-source JavaScript library introduced by Dan Abramov and Andrew Clark in 2015. It's designed to help manage the synchronous state of applications. It offers several benefits, including centralized state management, easier debugging, and strong community support.

Benefits of Redux:

  1. Centralized State Management:
    Redux provides a single store to manage all the state of your application, making it easier to access and update the data.
  2. Easy Debugging:
    You can track and log the state flow of Redux, which helps with debugging and reproducing bugs in your application.
  3. Community Support and Resources: 
    Redux has a large developer community that can assist with solving both complex and minor problems.

Mini-Project Description:

In this mini-project, we will create a simple form with fields for Name, Email, Mobile Number, Gender, and two buttons (Submit and Cancel). We'll use Redux to manage the application's state.

Here are the key concepts of Redux with the mini-project:

1. Action Types:

Action types are constants that represent the various actions your application can perform. It's good practice to maintain all action types in one file.

For example:

2. Action:

Actions are plain JavaScript objects that describe what should happen in your application. They must have a "type" property to specify the action and a "payload" property to pass data to reducers. 

For example:

3. Reducers:

Reducers are pure functions that specify how your application's state changes in response to actions. They take the initial state and an action as parameters. 

 For example:

In this example,We have a JavaScript Redux reducer for managing user data. It handles adding, updating, and deleting user records.

4. Store:
The store is a central repository for your application's state. It holds the entire state and provides access and updates. You create a store by passing your reducer to createStore. 

 For example:

In this mini-project, we've covered the basics of state management in Redux. 

To use the states in your React project, you'll need to use two hooks from react-redux:

1. useSelector:
useSelector is a hook used in functional components to access pieces of state from the global application state managed by Redux. It takes a selector function as an argument, defining how to extract data from the Redux store. When the data in the store changes, the component using useSelector will re-render to reflect the updated state.

import { useSelector } from "react-redux";

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

2. useDispatch:
useDispatch is a hook that provides access to the Redux store's dispatch function. It allows you to dispatch actions to update the state.

import { useDispatch } from "react-redux";

const dispatch = useDispatch();

With these hooks, you can interact with the Redux store and manage your application's state efficiently. Now we have already made the actionType, action, reducer and store now we have to use it in the React component.

import React, { useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { addUser, deleteUser, updateUser } from "../redux/action";

function DataDisplay() {
  const [visible, isVisible] = useState(false);
  const dispatch = useDispatch();
  const users = useSelector((state) => state.users);
  const [editMode, setEditMode] = useState(false);
  const [selectedID, setSelectedID] = useState(null);
  const [table, setTable] = useState(true);
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    mobile: "",
    gender: "male",
  });

  const handleInputChange = (e) => {
    const { name, value } = e.target;
    setFormData((prevData) => ({ ...prevData, [name]: value }));
  };

  const handleFormSubmit = (event) => {
    event.preventDefault();

    if (editMode) {
      dispatch(updateUser({ id: selectedID, ...formData }));
    } else {
      dispatch(addUser({ id: Date.now(), ...formData }));
    }

    setFormData({
      name: "",
      email: "",
      gender: "",
      mobile: "",
    });
    console.log(formData);
    isVisible(false);
    setSelectedID(false);
    setEditMode(false);
    setTable(true);
  };

  const handleDeleteUser = (userId) => {
    dispatch(deleteUser(userId));
  };

  const handleUpdateUser = (user) => {
    console.log(user);
    setFormData(user);

    setEditMode(true);
    isVisible(true);
    setSelectedID(user.id);
    setTable(false);
  };

  return (
    <>
      <div>
        <div className="flex justify-between p-4 shadow rounded-3xl">
          <div>
            <h1 className="text-3xl">Crud Operation</h1>
          </div>
          <div>
            <button
              onClick={() => isVisible(true)}
              className="border bg-green-500 p-2 pr-5 pl-5 text-lg rounded text-white"
            >
              {" "}
              New User
            </button>
          </div>
        </div>
        {visible && (
          <div>
            <form
              onSubmit={handleFormSubmit}
              className="border-black  w-fit relative m-auto z-100 p-12 top-2 outline-groove 
              bg-rgba(0.5, 0, 0, 0.5) rounded-lg shadow-lg"
            >
              <div className="">
               <input
                  type="text"
                  name="name"
                  value={formData.name}
                  onChange={handleInputChange}
                  required
                  placeholder="Enter your Name"
                  className="border border-gray-300 p-2 rounded-lg w-96 text-left"
                />
              </div>
              <div>
               <input
                  type="email"
                  name="email"
                  value={formData.email}
                  onChange={handleInputChange}
                  required
                  placeholder="Enter your Email"
                  className="border border-gray-300 p-2 rounded-lg w-96 m-2 text-left"
                />
              </div>
              <div>
                <input
                  type="number"
                  name="mobile"
                  value={formData.mobile}
                  onChange={handleInputChange}
                  required
                  placeholder="Enter your Mobile Number"
                  className="border border-gray-300 p-2 w-96 rounded-lg text-left"
                />
              </div>
              <div className="text-left pl-3">
                <span>Gender : </span>
                <input
                  type="radio"
                  name="gender"
                  value="male"
                  checked={formData.gender === "male"}
                  onChange={handleInputChange}
                  className="form-checkbox text-blue-500 h-5 w-5"
                />
                <span className="ml-2 text-gray-700">Male</span>
                <input
                  type="radio"
                  name="gender"
                  value="female"
                  checked={formData.gender === "female"}
                  onChange={handleInputChange}
                  className="form-checkbox text-blue-500 h-5 w-5 m-3"
                />
                <span className="ml-2 text-gray-700">Female</span>
              </div>

              <div className="mt-5 mb-0">
                <button
                  type="submit"
                  className="border bg-green-500 p-2 pr-5 pl-5 text-lg rounded text-white 
                  w-1/2"
                >
                  {editMode ? "Edit" : "Submit"}
                </button>
                <button
                  type="button"
                  onClick={() => isVisible(false)}
                  className="border bg-red-500 p-2 pr-5 pl-5 text-lg rounded text-white 
                  w-1/2"
                >
                  Cancel
                </button>
              </div>
            </form>
          </div>
        )}

        <>
          {users.length > 0 ? (
            table && (
              <table className="min-w-full mt-3 border rounded-lg p-6">
                <tr>
                  <th className="px-6 py-3 bg-gray-50 text-center text-xs leading-3 
                   font-medium text-gray-500 uppercase tracking-wider">
                    Name
                  </th>
                  <th className="px-6 py-3 bg-gray-50 text-center text-xs leading-4 
                   font-medium text-gray-500 uppercase tracking-wider">
                    Email
                  </th>
                  <th className="px-6 py-3 bg-gray-50 text-center text-xs leading-4 
                   font-medium text-gray-500 uppercase tracking-wider">
                    Mobile No
                  </th>
                  <th className="px-6 py-3 bg-gray-50 text-center text-xs leading-4 
                   font-medium text-gray-500 uppercase tracking-wider">
                    Gender
                  </th>
                  <th className="px-6 py-3 bg-gray-50 text-center text-xs leading-4 
                   font-medium text-gray-500 uppercase tracking-wider">
                    Action
                  </th>
                </tr>
                {users.map((user) => (
                  <tr key={user.id} className="text-center ml-6 p-8">
                    <td className="px-6 py-3 bg-white-50 text-center text-xs leading-3 
                     font-medium text-gray-500 uppercase tracking-wider">
                      {user.name}
                    </td>
                    <td className="px-6 py-3 bg-white-50 text-center text-xs leading-3 
                     font-medium text-gray-500 uppercase tracking-wider">
                      {user.email}
                    </td>
                    <td className="px-6 py-3 bg-white-50 text-center text-xs leading-3 
                     font-medium text-gray-500 uppercase tracking-wider">
                      {user.mobile}
                    </td>
                    <td className="px-6 py-3 bg-white-50 text-center text-xs leading-3 
                     font-medium text-gray-500 uppercase tracking-wider">
                      {user.gender}
                    </td>
                    <button
                      onClick={() => handleDeleteUser(user.id)}
                      className="border bg-red-500 p-1 pr-5 pl-5 text-lg rounded text-white"
                    >
                      Delete
                    </button>
                    <button
                      onClick={() => handleUpdateUser(user)}
                      className="border ml-1 bg-yellow-200 p-1 pr-5 pl-5 text-lg rounded 
                      text-black"
                    >
                      Edit
                    </button>
                  </tr>
                ))}
              </table>
            )
          ) : visible ? (
            ""
          ) : (
            <h1 className="p-10 text-3xl">No data found</h1>
          )}
        </>
      </div>
    </>
  );
}

export default DataDisplay;


Explanation of above code

1. Component State Management :

The component begins by importing the necessary dependencies and Redux actions for managing user data.

It uses the useState hook to manage various local states, such as whether the form is visible (visible), whether the component is in edit mode (editMode), and the selected user's ID (selectedID).

The table state is used to control the visibility of the user data table. formData is an object that holds the user's input data for the form fields.

2. Input Handling :

The handleInputChange function is responsible for updating the formData state when the user enters data into the form fields. It dynamically updates the form data based on the input's name and value.

3. Form Submission :

The handleFormSubmit function is triggered when the user submits the form. It dispatches actions to either add a new user or update an existing user based on whether the component is in edit mode. After submission, the form fields are cleared, and various state variables are reset to control the form's visibility and edit mode.

4. User Data Management :

Two functions, handleDeleteUser and handleUpdateUser, are responsible for interacting with the Redux store. handleDeleteUser dispatches the deleteUser action, which removes a user with a specified ID from the store. handleUpdateUser is used to edit an existing user's information. It sets the form data with the user's details and updates the component's state to enter edit mode.

5. Render Structure :

The component is structured with HTML elements and CSS classes to create a user-friendly interface. It includes a form for adding or editing users and a table to display user data. The form's visibility is controlled by the visible state.

The table is displayed when there is user data (users array) and the component is not in edit mode (table is true). User data is mapped and displayed in rows with buttons for deleting and editing users.

6. Redux Integration :

The component interacts with the Redux store through the useSelector and useDispatch hooks. useSelector is used to access the users array from the store, allowing the component to display and manipulate user data.

useDispatch provides access to the dispatch function, allowing the component to send actions to the store for adding, updating, or deleting users. 

By combining these elements, the component demonstrates how to use Redux for managing the state of a user data management system in a React application. It simplifies state management, making it easier to perform CRUD (Create, Read, Update, Delete) operations on user records.

This code showcases the power of Redux for maintaining application state and provides a user-friendly interface for managing user data efficiently.

Output would be like this: