Building a Simple CRUD Application with React and Redux - TechXplore

Friday, 17 May 2024

Building a Simple CRUD Application with React and Redux

 

In today's digital age, efficient data management is crucial for any web application. Whether you're dealing with user information, product details, or any other kind of data, having a seamless way to create, read, update, and delete (CRUD) data is fundamental. In this blog post, I'll walk you through the process of building a simple CRUD application using React and Redux, accompanied by custom CSS for styling.

 

What You'll Learn

  1. Setting Up a React Application: Learn the basics of creating a React project using Create React App.
  2. Integrating Redux: Understand how to manage state efficiently using Redux for a more scalable and maintainable application.
  3. CRUD Operations: Implement functionalities to view, add, edit, and delete data.
  4. Styling with CSS: Apply custom styles to enhance the user interface.

Why React and Redux?

React is a popular JavaScript library for building user interfaces, known for its component-based architecture and efficient rendering. Redux, on the other hand, is a predictable state container that works well with React, allowing you to manage your application's state in a more structured way. Together, they provide a powerful combination for building dynamic and responsive web applications.

Project Overview

In this project, we will create a simple table that allows users to perform CRUD operations. Here's a brief overview of the features:

  • View Data: Display a list of data entries in a tabular format.
  • Add New Data: Provide a form to input new data and add it to the table.
  • Edit Data: Enable users to modify existing data entries.
  • Delete Data: Allow users to remove unwanted data entries.

Project structure:

  • Inventroy.js 
  • Inventroy.css
  • inventroy.json
  • additem.js
  • additem.css
  • action.js
  • reducer.js
  • app.js

Technologies Used

  • React: For building the user interface.
  • Redux: For managing the application state.
  • CSS: For styling the components.
  • React router: Redirect to the next page.


Getting Started

To get started, ensure you have Node.js installed on your machine. We'll use Create React App to set up the initial project structure. Follow these steps to create your project:

npx create-react-app crud-app cd crud-app npm install redux react-redux



Installing React Router is an essential step in setting up client-side routing for your React application. Here's a step-by-step guide on how to install React Router:

Step 1: Install React Router Dom Package

React Router Dom is the most popular routing library for React applications. You can install it via npm or yarn. Open your terminal and run one of the following commands:

Using npm:

npm install react-router-dom

Step 2: Verify Installation

Once the installation is complete, make sure React Router Dom is added as a dependency in your package.json file.



First, we need to define the types of actions our application will handle. Action types are constants that represent the different kinds of actions that can be dispatched to the Redux store.

Step 1: Adding JSON Data for Inventory Items

To begin building our Inventory Management System, we need to define the inventory items. Below is the JSON data representing a sample list of items:

[ { "id": 1, "itemName": "Coffee", "Price": "20$", "quantity": 4, "sold": 4 }, { "id": 2, "itemName": "Tea", "Price": "14$", "quantity": 3, "sold": 4 }, { "id": 3, "itemName": "Cold Coffee", "Price": "34$", "quantity": 6, "sold": 3 }, { "id": 4, "itemName": "Burger", "Price": "30$", "quantity": 4, "sold": 3 } ]



action.js


export  const ADD_ITEM ="ADD_ITEM";
export const DELETE_ITEM="DELETE_ITEM";
export const UPDATE_ITEM="UPDATE_ITEM";



export const addItem = (item) => ({
  type: "ADD_ITEM",
  payload: item,
});



export const deleteItem = (itemName) => {
   return {
     type: DELETE_ITEM,
     payload: itemName, 
   };
 };
 
 
 
 export const updateItem = (item) => ({
  type: UPDATE_ITEM,
  payload: item,
});

In the code above, we have defined three action types:

  • ADD_ITEM: Action type for adding a new item.
  • DELETE_ITEM: Action type for deleting an existing item.
  • UPDATE_ITEM: Action type for updating an existing item.


  • addItem: Takes an item as an argument and returns an action with the type ADD_ITEM and the item as the payload.
  • deleteItem: Takes an itemName as an argument and returns an action with the type DELETE_ITEM and the item name as the payload. This is used to identify which item to delete.
  • updateItem: Takes an item as an argument and returns an action with the type UPDATE_ITEM and the item as the payload. This allows us to update the item’s details.

we will set up our Redux store and define a reducer to handle our CRUD operations. This will allow us to manage the state of our items seamlessly. We'll start by creating our initial state and then defining our reducer function to handle the different actions.

Step 1: Define the Initial State

Our initial state will contain the list of items, which we’ll import from a JSON file.

// store.js import { ADD_ITEM, DELETE_ITEM, UPDATE_ITEM } from "./action"; import { createStore } from "redux"; import JSON from "./InventroyData.json"; const initialState = { items: JSON, };


Step 2: Create the Reducer

Next, we define our reducer function. The reducer takes the current state and an action as arguments and returns the new state based on the action type.

const itemReducer = (state = initialState, action) => { switch (action.type) { case ADD_ITEM: return { ...state, items: [...state.items, action.payload], }; case DELETE_ITEM: return { ...state, items: state.items.filter((item) => item.id !== action.payload.id), }; case UPDATE_ITEM: return { ...state, items: state.items.map((item) => { if (item.id === action.payload.id) { return { ...item, itemName: action.payload.itemName, price: action.payload.price, quantity: action.payload.quantity, sold: action.payload.sold, }; }

return item; }), }; default: return state; } };

Step 3: Create the Redux Store

Finally, we create the Redux store using the createStore function from Redux and pass our reducer to it.

const store = createStore(itemReducer); export default store;

Explanation of the Reducer

  • ADD_ITEM: When an ADD_ITEM action is dispatched, the reducer adds the new item (contained in action.payload) to the current list of items.
  • DELETE_ITEM: For a DELETE_ITEM action, the reducer filters out the item with the matching id from the current list of items.
  • UPDATE_ITEM: When an UPDATE_ITEM action is dispatched, the reducer maps through the items, finds the item with the matching id, and updates its properties based on the action.payload.
Next ,we will create an Inventory Page. This system will enable users to view, edit, and delete items from an inventory list. Follow the steps below to understand how each part of the system is implemented.

Step 1: Define the Inventory Component

First, we define the Inventory component, which will be responsible for displaying and managing the inventory items.

import React from "react";
import { connect } from "react-redux";
import { deleteItem, updateItem } from "./action";
import { Link } from 'react-router-dom';
import "./inventroy.css";


Step 2: Set Up the Initial State

We set up the initial state in the component's constructor. This state will manage the visibility of edit and delete popups, the selected item for editing, and the input values for updating an item.

class Inventory extends React.Component { constructor(props) { super(props); this.state = { showEditPopup: false, showDeletePopup: false, selectedItem: null, newName: "", newPrice: "", newQuantity: "", newSold: "", }; }


Step 3: Handle Edit Popup

We create a method to handle the edit popup. This method sets the state to show the edit popup and populates it with the details of the selected item.

handleEditPopup = (item) => { this.setState({ showEditPopup: true, selectedItem: item, newName: item.itemName, newPrice: item.Price, newQuantity: item.quantity, newSold: item.sold, }); };


Step 4: Handle Update Item

This method handles the update action. It updates the selected item with new values and dispatches the updateItem action.

handleUpdateItem = () => { const { selectedItem, newName, newPrice, newQuantity, newSold } = this.state; if (selectedItem) { const updatedItem = { ...selectedItem, itemName: newName || selectedItem.itemName, Price: newPrice || selectedItem.Price, quantity: newQuantity || selectedItem.quantity, sold: newSold || selectedItem.sold, }; this.props.updateItem(updatedItem); this.handleCancelPopup(); } };

Step 5: Handle Input Change

We define a method to handle input changes. This method updates the state with the new input values.

handleInputChange = (e) => { this.setState({ [e.target.name]: e.target.value }); };


Step 6: Handle Delete Item

This method handles the delete action. It deletes the selected item by dispatching the deleteItem action.

handleDeleteItem = () => { const { itemToDelete } = this.state; if (itemToDelete) { this.props.deleteItem(itemToDelete); this.handleCancelPopup(); } };


Step 7: Handle Delete Popup

This method sets the state to show the delete popup and stores the item to be deleted.

handleDeletePopup = (item) => { this.setState({ showDeletePopup: true, itemToDelete: item }); };

Step 8: Handle Cancel Popup

This method handles the cancellation of popups by resetting the relevant state variables.

handleCancelPopup = () => { this.setState({ showDeletePopup: false, showEditPopup: false, selectedItem: null, }); };

Step 9: Render Method

We define the render method to display the inventory items and the edit/delete popups.

render() { const { showDeletePopup, showEditPopup } = this.state; return ( <div> <h1>Inventory</h1> <Link to='/Add'> <button className="Add">Add</button> </Link> <table> <thead> <tr> <th>Product Name</th> <th>Price</th> <th>Quantity</th> <th>Sold</th> <th>Action</th> </tr> </thead> <tbody> {this.props.items.map((item) => ( <tr key={item.id}> <td>{item.itemName}</td> <td>{item.Price}</td> <td>{item.quantity}</td> <td>{item.sold}</td> <td> <button className="Edit" onClick={() => this.handleEditPopup(item)}>Edit</button> <button className="Delete" onClick={() => this.handleDeletePopup(item)}>Delete</button> </td> </tr> ))} </tbody> </table> {showEditPopup && ( <div className="Popup"> <div className="Popup-content"> <p>Edit Your Data</p> <div className="EditPopup"> <div className="editlabel"> <label>Product Name</label> <label>Price</label> <label>Quantity</label> <label>Sold</label> </div> <div className="editbox"> <input type="text" name="newName" value={this.state.newName} onChange={this.handleInputChange} /> <input type="text" name="newPrice" value={this.state.newPrice} onChange={this.handleInputChange} /> <input type="text" name="newQuantity" value={this.state.newQuantity} onChange={this.handleInputChange} /> <input type="text" name="newSold" value={this.state.newSold} onChange={this.handleInputChange} /> </div> </div> <div className="Popup-button"> <button className="yes" onClick={this.handleUpdateItem}>Update</button> <button className="cancel" onClick={this.handleCancelPopup}>Cancel</button> </div> </div> </div> )} {showDeletePopup && ( <div className="Popup"> <div className="Popup-content"> <p>Are you sure you want to delete?</p> <div className="Popup-button"> <button className="yes" onClick={this.handleDeleteItem}>Yes</button> <button className="cancel" onClick={this.handleCancelPopup}>Cancel</button> </div> </div> </div> )} </div> ); } } const mapStateToProps = (state) => ({ items: state.items, }); const mapDispatchToProps = (dispatch) => ({ deleteItem: (item) => dispatch(deleteItem(item)), updateItem: (item) => dispatch(updateItem(item)), }); export default connect(mapStateToProps, mapDispatchToProps)(Inventory);

This system allows users to view, edit, and delete items seamlessly.

Next step added the css file:

body{ margin: 1px; padding: 2px; } h1{ text-align: center; text-transform: uppercase; color: green; } .Add{ margin-left: 91%; background-color: rgb(121, 195, 11); color: white; padding: 10px 40px; font-size: 17px; font-weight: bold; cursor: pointer; border: none; border-radius: 20px; } .Add:hover{ background-color: antiquewhite; color:black } table{ width: 100%; border-collapse: collapse; margin-top: 10px; } th,td{ padding: 10px; border:1px solid black; text-align: center; } th{ background-color: aliceblue; } .Edit{ padding: 12px 30px; background-color: green; color: white; cursor: pointer; border: none; border-radius: 14px; font-size: 16px; font-weight: bold; margin: 2px; } .Delete{ padding: 12px 30px; background-color: red; color: white; cursor: pointer; border: none; border-radius: 14px; font-size: 16px; font-weight: bold; } .Popup{ position: fixed; top: 0; left: 0; width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; background-color: rgb(0,0,0,0.5); } .Popup-content{ background-color: #fff; padding: 22px; border-radius:10px; box-shadow: 0px 2px 4px rgb(0,0, 0, 0.1); } .Popup-button{ display: flex; flex-direction: row; justify-content: space-between; } .yes{ padding: 10px; color: white; background-color: rgb(10, 247, 10); font-family: sans-serif; font-weight: bold; font-size: 17px; border: none; border-radius: 10px; cursor: pointer; } .cancel{ padding: 10px; border: none; font-family: sans-serif; font-weight: bold; font-size: 17px; color: white; background-color: blue; border: none; border-radius: 10px; cursor: pointer; } .EditPopup{ display: flex; flex-direction: row; justify-content: space-around; } .editlabel{ display: flex; flex-direction: column; } .editbox{ display: flex; flex-direction: column; } p{ text-align: center; color: black; font-family: sans-serif; font-weight: bold; } label{ padding: 20px; color: black; font-family: sans-serif; font-weight: bold; font-size: 17px; margin: 5px; } input{ padding: 20px 30px; margin: 5px; border: none; border-radius: 10px; box-shadow: 3px 3px 3px 3px rgb(185, 184, 184); }

We have already covered how to view, edit, and delete items from the inventory. Now, let's add the functionality to add new items to the inventory.


Step 1: Define the AddItem Component

The AddItem component allows users to add new items to the inventory. Here's the complete implementation:

import React from "react";

import "./additem.css";

import { Link } from 'react-router-dom';

import { addItem } from "./action";

import { v4 as uuidv4 } from 'uuid';

import { connect } from 'react-redux';

Step 2: Setting Up the Initial State

In the AddItem component, we initialize the state to manage the input values for the new item.

constructor(props) {

  super(props);

  this.state = {

    newName: "",

    newPrice: "",

    newQuantity: "",

    newSold: "",

  };

}

Step 3: Handle Adding a New Item

We create a method to handle the addition of a new item. This method collects the input values, creates a new item object, and dispatches the addItem action.

handleAddItem = () => { const { newName, newPrice, newQuantity, newSold } = this.state; if (newName && newPrice && newQuantity && newSold) { const newItem = { id: uuidv4(), itemName: newName, Price: newPrice, quantity: newQuantity, sold: newSold }; this.props.addItem(newItem); this.setState({ newName: "", newPrice: "", newQuantity: "", newSold: "", }); // Checking the new item in the console console.log("this is newitem " + newItem); } else { console.log(Error); } }

Step 4: Handle Input Changes

This method updates the state with the values entered by the user in the input fields.

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

Step 5: Render Method

The render method displays the form for adding a new item and includes buttons to add the item or cancel the action.

render() { return ( <div> <h1>Add New Product</h1> <div className="Additemcontainer"> <div className="lablesName"> <label>Product Name</label> <label>Price</label> <label>Quantity</label> <label>Sold</label> </div> <div className="NewItemName"> <input type="text" name="newName" value={this.state.newName} onChange={this.handleInputChange} /> <input type="text" name="newPrice" value={this.state.newPrice} onChange={this.handleInputChange} /> <input type="text" name="newQuantity" value={this.state.newQuantity} onChange={this.handleInputChange} /> <input type="text" name="newSold" value={this.state.newSold} onChange={this.handleInputChange} /> </div> </div> <div className="Buttons"> <Link to='/added'> <button className="addbtn" onClick={this.handleAddItem}>Add</button> </Link> <Link to='/cancel'> <button className="cancelbtn">Cancel</button> </Link> </div> </div> ); }

Step 6: Map Dispatch to Props

We connect the AddItem component to the Redux store and map the addItem action to the component's props.

const mapDispatchToProps = (dispatch) => ({ addItem: (item) => dispatch(addItem(item)), }); export default connect(null, mapDispatchToProps)(Additem);

Next step if you added the css file:
additem.css:
.Additemcontainer{
   display: flex;
   flex-direction: row;
   justify-content: center;
}
.lablesName{
   display: flex;
   flex-direction: column;
}
.NewItemName{
   display: flex;
   flex-direction: column;
}
label,input{
   margin: 10px;
}
.Buttons{
   display: flex;
   flex-direction: row;
   justify-content: center;
   margin-top: 10px;
}
.addbtn{
   padding: 10px 20px;
   margin: 5px;
   color: white;
   background-color: rgb(7, 189, 7);
   font-family: sans-serif;
   font-weight: bold;
   font-size: 17px;
   border: none;
   border-radius: 10px;
   cursor: pointer;
}
.addbtn:hover{
   background-color: rgb(134, 241, 122);
   color: black;
}
.cancelbtn{
   padding: 10px 20px;
   margin: 5px;
   border: none;
   font-family: sans-serif;
   font-weight: bold;
   font-size: 17px;
   color: white;
   background-color: blue;
   border: none;
   border-radius: 10px;
   cursor: pointer;
}
.cancelbtn:hover{
   background-color: rgb(63, 66, 246);
   color: black;

Integrating Components into the Main Application

Now that we have our components and Redux store set up, it's time to integrate everything into the main application. The App.js file is where we bring together all parts of our application, set up the routing, and wrap our application with the Redux Provider to make the store available to all components.

App.js

Here's the complete App.js file:

import Inventory from "./inventory"; import Additem from "./additem"; import { Provider } from 'react-redux'; import store from "./reducer"; import { Route, Routes } from 'react-router-dom'; function App() { return ( <Provider store={store}> <div> <Routes> <Route path="/" element={<Inventory />} /> <Route path="Add" element={<Additem />} /> <Route path="cancel" element={<Inventory />} /> <Route path="added" element={<Inventory />} /> </Routes> </div> </Provider> ); } export default App;

  1. Importing Components and Modules:

    • Inventory Component: The main component for displaying the inventory list.
    • Additem Component: The component for adding a new item to the inventory.
    • Provider: From react-redux, this is used to make the Redux store available to all container components in the application.
    • store: The Redux store, imported from the reducer file.
    • Route, Routes: From react-router-dom, used for setting up routing in the application.
  2. Wrapping the Application with Provider:

    • The Provider component is used to wrap the entire application, passing the Redux store to it. This makes the Redux store available to any nested components that are connected to it using connect from react-redux.
  3. Setting Up Routes:

    • Routes: Contains all the Route components.
    • Route Components:
      • / (root path) renders the Inventory component.
      • /Add renders the Additem component.
      • /cancel renders the Inventory component.
      • /added renders the Inventory component.

    These routes ensure that different components are rendered based on the URL path. This allows navigation between viewing the inventory and adding a new item.

Final Step: Setting Up the Entry Point

In this final step, we will set up the entry point of our React application. The index.js file will render our main App component into the DOM and configure the BrowserRouter for routing.

index.js

Here is the complete index.js file:

import React from 'react'; import './index.css'; import App from './App'; import { createRoot } from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; import reportWebVitals from './reportWebVitals'; const root = createRoot(document.getElementById('root')); root.render( <BrowserRouter> <App/> </BrowserRouter> ); reportWebVitals();

The index.js file is the entry point of the React application. It sets up the BrowserRouter to enable routing and renders the App component into the root DOM node. This completes the setup of your Inventory Management System.

By the end of this tutorial, you'll have a functional CRUD application that you can further extend and customize based on your needs. Whether you're a beginner looking to learn React and Redux or an experienced developer seeking to brush up on your skills, this project will provide a solid foundation for building more complex applications.

































No comments:

Post a Comment