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
- Setting Up a React Application: Learn the basics of creating a React project using Create React App.
- Integrating Redux: Understand how to manage state efficiently using Redux for a more scalable and maintainable application.
- CRUD Operations: Implement functionalities to view, add, edit, and delete data.
- 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
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.
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
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 typeADD_ITEM
and the item as the payload. - deleteItem: Takes an
itemName
as an argument and returns an action with the typeDELETE_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 typeUPDATE_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 inaction.payload
) to the current list of items. - DELETE_ITEM: For a
DELETE_ITEM
action, the reducer filters out the item with the matchingid
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 matchingid
, and updates its properties based on theaction.payload
.
Step 1: Define the Inventory Component
First, we define the Inventory
component, which will be responsible for displaying and managing the inventory items.
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);
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);
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;
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.
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 usingconnect
fromreact-redux
.
- The
Setting Up Routes:
- Routes: Contains all the
Route
components. - Route Components:
/
(root path) renders theInventory
component./Add
renders theAdditem
component./cancel
renders theInventory
component./added
renders theInventory
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.
- Routes: Contains all the
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();
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.
No comments:
Post a Comment