From Zero to Full Stack | A Simple Yet Fully Functional React, Django, and MySQL App


In this series of 2 articles, we will build a basic application using the React + Django + MySQL stack. The system will include user authentication, product CRUD operations, and communication between the frontend and backend.
Missed the first part? Check it out!
Let’s contine with the frontend…
Prerequisites
Before starting, you need to have installed:
Our Django API Server integrated with MySQL Database
Postman or Insomnia to test the API
Node.js and npm/yarn
To check all instalations:
node -v
npm -v
Setting Up the React Project
To create the React application, run the following command:
npx create-react-app frontend
cd frontend
Then, install the necessary dependencies:
npm install axios react-router-dom
axios → To make HTTP requests to our Django API.
react-router-dom → To handle navigation between pages.
Now, start and try the React application:
npm start
Project Structure
Inside the
frontend/src/
folder, create the following directories:
react_django/
│── backend/
│ │── api/
│ │── backend/
│ │── static/
│ │── django/
│
│── frontend/
│ │── node_modules/
│ │── public/
│ │── src/
│ │ │── components/ <----
│ │ │── pages/ <----
│ │ │── services/ <----
Setting Up The Routes (
App.js
)
// Importing React library to create components
import React from "react";
// Importing necessary components from React Router
// BrowserRouter (Router) → Enables client-side routing
// Routes → Wraps all defined routes
// Route → Defines individual paths and their corresponding components
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
// Importing page components for different routes
import ProductList from "./pages/ProductList"; // Displays the list of products
import ProductDetail from "./pages/ProductDetail"; // Shows details of a specific product
import EditProduct from "./pages/EditProduct"; // Allows editing an existing product
import CreateProduct from "./pages/CreateProduct"; // Provides a form to create a new product
import Login from "./pages/Login"; // Login page component
// Defining the main App component
const App = () => {
return (
// Wrapping the application with the Router to enable navigation
<Router>
{/* Defining the application routes */}
<Routes>
{/* Route for the login page (default/home page) */}
<Route path="/" element={<Login />} />
{/* Route for listing all products */}
<Route path="/products" element={<ProductList />} />
{/* Route for viewing details of a specific product, using a dynamic ":id" parameter */}
<Route path="/product/:id" element={<ProductDetail />} />
{/* Route for editing a specific product, identified by ":id" */}
<Route path="/edit-product/:id" element={<EditProduct />} />
{/* Route for creating a new product */}
<Route path="/create-product" element={<CreateProduct />} />
</Routes>
</Router>
);
};
// Exporting the App component as the default export
export default App;
Setting Up API Service (
services/api.js
)
We will create a centralized API service to handle authentication and product requests.
Create src/services/api.js
:
// Importing the Axios library for making HTTP requests
import axios from "axios";
// Defining the base URL for the API
const API_URL = "http://localhost:8000/api/";
// Creating an Axios instance with predefined settings
const api = axios.create({
baseURL: API_URL, // Setting the base URL for all requests
headers: {
"Content-Type": "application/json", // Ensuring requests use JSON format
},
});
// Function to set the Authorization token in request headers
export const setAuthToken = (token) => {
if (token) {
// If a token exists, set it in the Authorization header
api.defaults.headers.common["Authorization"] = `Token ${token}`;
} else {
// If no token is provided, remove the Authorization header
delete api.defaults.headers.common["Authorization"];
}
};
// Retrieve the saved authentication token from local storage when the page loads
const savedToken = localStorage.getItem("token");
if (savedToken) {
setAuthToken(savedToken); // Apply the token if it exists
}
// Function to log in the user by sending credentials and storing the token
export const loginUser = async (credentials) => {
const response = await api.post("token/", credentials); // Send login request
localStorage.setItem("token", response.data.token); // Save token in local storage
setAuthToken(response.data.token); // Apply token for future requests
return response.data; // Return the response data
};
// Function to retrieve the user ID from local storage and convert it to an integer
export const getUserId = () => {
const userId = localStorage.getItem("user_id"); // Get user ID from local storage
return userId ? parseInt(userId, 10) : null; // Convert to integer or return null
};
// Function to create a new product and handle errors properly
export const createProduct = async (productData) => {
try {
// Send request to create product
const response = await api.post("products/", productData);
// Return the created product data
return response.data;
} catch (error) {
// Log and handle any errors during the request
console.error("Error creating product:", error.response ? error.response.data : error.message);
throw new Error(error.response ? JSON.stringify(error.response.data) : "Unknown error occurred");
}
};
// Function to fetch all products with authentication check
export const getProducts = async () => {
// Retrieve the authentication token
const token = localStorage.getItem("token");
if (!token) {
// Prevent request if no token is present
throw new Error("No authentication token found");
}
// Fetch products
return api.get("products/")
// Return the response data
.then((response) => response.data).catch((error) => {
// Log errors
console.error("Error fetching products:", error.response?.data || error.message);
throw error; // Throw the error for further handling
});
};
// Function to fetch a single product by its ID
export const getProductById = async (id) => {
const response = await api.get(`products/${id}/`); // Fetch product by ID
return response.data; // Return product data
};
// Function to update an existing product by its ID
export const updateProduct = async (id, productData) => {
const response = await api.put(`products/${id}/`, productData); // Send update request
return response.data; // Return updated product data
};
// Function to delete a product by its ID
export const deleteProduct = async (id) => {
await api.delete(`products/${id}/`); // Send delete request
};
// Exporting the API instance for use in other parts of the application
export default api;
This JavaScript module uses Axios to handle HTTP requests to a REST API, managing user authentication and CRUD operations for products. It defines a base API URL and creates an Axios instance with default settings. The setAuthToken
function sets or removes the authentication token for requests. The loginUser
function sends user credentials to retrieve and store a token, which is applied for future requests. The module includes functions to fetch, create, update, and delete products, ensuring authentication before making requests. Errors are handled properly, and the token is stored in localStorage for persistence. This setup is commonly used in React applications interacting with Django REST Framework or similar APIs.
Creating the Login Page (
pages/Login.js
)
// Import necessary dependencies from React
import React, { useState } from "react";
// Import `useNavigate` from React Router to handle navigation (redirection)
import { useNavigate } from "react-router-dom";
// Import the API function to authenticate the user
import { loginUser } from "../services/api";
// Define the `Login` component
const Login = () => {
// State to store the username input
const [username, setUsername] = useState("");
// State to store the password input
const [password, setPassword] = useState("");
// State to store any login error messages
const [error, setError] = useState("");
// Hook to enable navigation (redirection) after successful login
const navigate = useNavigate();
// Function to handle user login
const handleLogin = async (e) => {
e.preventDefault(); // Prevent the default form submission behavior
try {
// Call the API function to authenticate the user
const data = await loginUser({ username, password });
// Store authentication token in localStorage
localStorage.setItem("token", data.token);
// Store user ID in localStorage for future requests
localStorage.setItem("user_id", data.user_id);
// Dispatch a `storage` event to update other components that rely on authentication
window.dispatchEvent(new Event("storage"));
// Redirect the user to the products page after successful login
navigate("/products");
} catch (err) {
// If an error occurs, update the error state with a message
setError("Invalid credentials. Please try again.");
}
};
return (
<div>
{/* Page heading */}
<h2>Login</h2>
{/* Display error message if login fails */}
{error && <p style={{ color: "red" }}>{error}</p>}
{/* Login form */}
<form onSubmit={handleLogin}>
<div>
{/* Label and input field for the username */}
<label>Username:</label>
<input
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)} // Update state when input changes
required
/>
</div>
<div>
{/* Label and input field for the password */}
<label>Password:</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)} // Update state when input changes
required
/>
</div>
{/* Submit button to trigger login */}
<button type="submit">Login</button>
</form>
</div>
);
};
// Export the `Login` component so it can be used in other parts of the app
export default Login;
This React Login component handles user authentication using the loginUser
function from an external API service. It uses state hooks to manage user input (username
, password
), an error
message, and useNavigate
for redirection. When the user submits the form, it prevents default submission, calls the API with credentials, and if successful, stores the authentication token and user ID in localStorage, triggers a "storage"
event to update other components, and redirects the user to the products page. If authentication fails, an error message is displayed. This component provides a simple, functional login interface for a React application.
Creating the Product Detail Page (
pages/ProductDetail.js
)
// Import necessary dependencies from React
import React, { useEffect, useState } from "react";
// Import `useParams` from React Router to get URL parameters
import { useParams } from "react-router-dom";
// Import API function to fetch product details by ID
import { getProductById } from "../services/api";
// Import Navbar component for navigation
import Navbar from "../components/Navbar";
// Import Loading component to show a loading indicator
import Loading from "../components/Loading";
// Define the `ProductDetail` component
const ProductDetail = () => {
// Get the `id` parameter from the URL using React Router
const { id } = useParams();
// State to store the product details
const [product, setProduct] = useState(null);
// State to manage loading status
const [loading, setLoading] = useState(true);
// useEffect hook to fetch product details when the component mounts or `id` changes
useEffect(() => {
// Define an async function to fetch product details
const fetchProduct = async () => {
try {
// Call the API function to fetch product data by ID
const data = await getProductById(id);
// Store the retrieved product data in the state
setProduct(data);
} catch (error) {
// Log an error message if fetching fails
console.error("Error fetching product:", error);
} finally {
// Set loading to false after API call completes
setLoading(false);
}
};
// Call the fetch function when the component mounts
fetchProduct();
}, [id]); // Re-run this effect when `id` changes
return (
<div>
{/* Render Navbar component */}
<Navbar />
{/* Show loading indicator if still fetching product details */}
{loading ? <Loading /> : (
<div>
{/* Display product name */}
<h2>{product.name}</h2>
{/* Display product description */}
<p>{product.description}</p>
{/* Display product price */}
<p><strong>Price:</strong> ${product.price}</p>
</div>
)}
</div>
);
};
// Export the `ProductDetail` component so it can be used in other parts of the app
export default ProductDetail;
This React ProductDetail component fetches and displays details of a specific product based on the ID from the URL. It uses useParams
to extract the ID, state hooks to manage product data and loading status, and useEffect
to fetch product details when the component mounts or when the ID changes. The API call to getProductById(id)
retrieves the product data, which is then stored in state. While loading, a Loading indicator is displayed; once fetched, the product name, description, and price are shown. It also includes a Navbar for navigation. This component provides a dynamic product details page in a React application.
Creating the Product List Page (
pages/ProductList.js
)
// Import necessary dependencies from React
import React, { useEffect, useState, useCallback } from "react";
// Import `useNavigate` from React Router to handle page redirection
import { useNavigate } from "react-router-dom";
// Import API function to fetch all products
import { getProducts } from "../services/api";
// Import Navbar component for navigation
import Navbar from "../components/Navbar";
// Import ProductCard component to display individual products
import ProductCard from "../components/ProductCard";
// Import Loading component to show a loading indicator
import Loading from "../components/Loading";
// Define the `ProductList` component
const ProductList = () => {
// State to store the list of products
const [products, setProducts] = useState([]);
// State to track the loading status
const [loading, setLoading] = useState(true);
// Hook to enable navigation (redirection)
const navigate = useNavigate();
// Function to fetch products from the API
// Wrapped in useCallback to prevent unnecessary re-creation of the function
const fetchProducts = useCallback(async () => {
// Retrieve authentication token from localStorage
const token = localStorage.getItem("token");
// If no token is found, the user is not authenticated, so redirect to login
if (!token) {
console.log("User not authenticated. Redirecting to login...");
navigate("/"); // Redirect to login page
return;
}
try {
console.log("Fetching products...");
setLoading(true); // Show loading before making the API request
// Call the API function to fetch all products
const data = await getProducts();
// Update the state with the fetched products
setProducts(data);
} catch (error) {
// Log an error message if fetching fails
console.error("Error fetching products:", error);
// If the error status is 401 (Unauthorized), redirect to login page
if (error.response?.status === 401) {
console.log("Unauthorized request. Redirecting to login...");
navigate("/");
}
} finally {
// Set loading to false after API call completes
setLoading(false);
}
}, [navigate]); // Memoized fetchProducts depends only on `navigate`
// useEffect to trigger `fetchProducts()` when the page loads
useEffect(() => {
fetchProducts();
}, [fetchProducts]); // Runs whenever fetchProducts changes (but it is stable due to useCallback)
const handleDelete = (deletedProductId) => {
setProducts((prevProducts) => prevProducts.filter((product) => product.id !== deletedProductId));
};
return (
<div>
{/* Render Navbar component */}
<Navbar />
{/* Page heading */}
<h2>My Products</h2>
{/* Show loading indicator if still fetching products */}
{loading ? (
<Loading />
) :
/* If products exist, display them in ProductCard components */
products.length > 0 ? (
products.map((product) => (
// <ProductCard key={product.id} product={product} />
<ProductCard key={product.id} product={product} onDelete={handleDelete} />
))
) : (
/* Show a message if no products are found */
<p>No products found.</p>
)}
</div>
);
};
// Export the `ProductList` component so it can be used in other parts of the app
export default ProductList;
This React ProductList component fetches and displays a list of products, ensuring that only authenticated users can access it. It uses useState
to manage the product list and loading state, useEffect
to fetch products when the component mounts, and useCallback
to optimize the fetchProducts function. If no authentication token is found in localStorage
, the user is redirected to the login page. Products are retrieved via the getProducts
API call and displayed using the ProductCard component. If fetching fails with a 401 (Unauthorized) error, the user is also redirected to login. The component includes a Navbar and a loading indicator, and it allows products to be removed from the list dynamically via the handleDelete
function. This ensures an efficient, authenticated product listing page in a React application.
Create the Create Product Page (
pages/CreateProduct.js
)
// Import necessary modules from React and React Router
// useState is used for state management
import React, { useState } from "react";
// useNavigate is used to programmatically navigate to other pages
import { useNavigate } from "react-router-dom";
// Import the API function to create a new product
import { createProduct } from "../services/api";
// Import the Navbar component
import Navbar from "../components/Navbar";
// Define the CreateProduct component
const CreateProduct = () => {
// Initialize navigation for redirecting after product creation
const navigate = useNavigate();
// State to store product details (name, description, price)
const [product, setProduct] = useState({
name: "",
description: "",
price: "",
});
// State to store error messages
const [error, setError] = useState("");
// State to manage loading status (for UI feedback)
const [loading, setLoading] = useState(false);
// Function to handle changes in the input fields
const handleChange = (e) => {
// Update the corresponding field in the product state
setProduct({ ...product, [e.target.name]: e.target.value });
};
// Function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault(); // Prevent default form submission behavior
setError(""); // Clear any previous error messages
setLoading(true); // Set loading state to true while making API request
try {
// Call API function to create a new product
await createProduct(product);
navigate("/products"); // Redirect to the product list page after successful creation
} catch (err) {
console.error("Product creation error:", err.message); // Log the error in the console
setError(err.message); // Update the error state to display on UI
} finally {
setLoading(false); // Reset loading state after request completion
}
};
return (
<div>
{/* Include the Navbar component at the top of the page */}
<Navbar />
{/* Page heading */}
<h2>Create New Product</h2>
{/* Display error message if any */}
{error && <p style={{ color: "red" }}>{error}</p>}
{/* Form for creating a new product */}
<form onSubmit={handleSubmit}>
<div>
{/* Input field for product name */}
<label>Name:</label>
<input
type="text"
name="name"
value={product.name}
onChange={handleChange} // Update state on input change
required
/>
</div>
<div>
{/* Textarea for product description */}
<label>Description:</label>
<textarea
name="description"
value={product.description}
onChange={handleChange} // Update state on input change
required
/>
</div>
<div>
{/* Input field for product price */}
<label>Price:</label>
<input
type="number"
name="price"
step="0.01" // Allows decimal values (e.g., 19.99)
value={product.price}
onChange={handleChange} // Update state on input change
required
/>
</div>
{/* Submit button for creating a product */}
<button type="submit" disabled={loading}>
{/* Change button text dynamically based on loading state */}
{loading ? "Creating..." : "Create Product"}
</button>
</form>
</div>
);
};
// Export the component for use in other parts of the application
export default CreateProduct;
This React CreateProduct component allows users to create a new product by submitting a form. It uses useState
to manage product details (name, description, price), error messages, and loading state, and useNavigate
to redirect users to the product list after successful creation. The handleChange
function updates the state as users type, while handleSubmit
sends the data to the API via createProduct
. If the request fails, an error message is displayed. The button dynamically changes its text based on the loading state. The Navbar is included for navigation, ensuring a user-friendly product creation interface in a React application.
Create the Edit Product Page (
pages/EditProduct.js
)
// Import necessary modules from React and React Router
// useState for managing state, useEffect for fetching data on component load
import React, { useState, useEffect } from "react";
// useParams to get product ID from the URL, useNavigate for redirection
import { useParams, useNavigate } from "react-router-dom";
// Import API functions to fetch and update product details
import { getProductById, updateProduct } from "../services/api";
// Define the EditProduct component
const EditProduct = () => {
// Extract the product ID from the URL parameters
const { id } = useParams();
// Initialize navigate for programmatic redirection
const navigate = useNavigate();
// State to store the product data
const [product, setProduct] = useState({ name: "", description: "", price: "" });
// useEffect runs when the component mounts or when the ID changes
useEffect(() => {
const fetchProduct = async () => {
// Fetch the product data from the API using the ID
const data = await getProductById(id);
setProduct(data); // Set the retrieved product data in state
};
fetchProduct(); // Call the function to fetch the product
}, [id]); // Dependency array ensures the effect runs when `id` changes
// Function to handle form submission
const handleSubmit = async (e) => {
e.preventDefault(); // Prevent the default form submission behavior
// Call API function to update the product with the new values
await updateProduct(id, product);
// Redirect back to the product list after successful update
navigate("/products");
};
return (
<form onSubmit={handleSubmit}>
{/* Input field for the product name */}
<input
type="text"
value={product.name}
onChange={(e) => setProduct({ ...product, name: e.target.value })}
// Update state when input changes
required
/>
{/* Textarea for the product description */}
<textarea
value={product.description}
onChange={(e) => setProduct({ ...product, description: e.target.value })}
// Update state when input changes
required
/>
{/* Input field for the product price */}
<input
type="number"
value={product.price}
onChange={(e) => setProduct({ ...product, price: e.target.value })}
// Update state when input changes
required
/>
{/* Submit button to save changes */}
<button type="submit">Save Changes</button>
</form>
);
};
// Export the component for use in other parts of the application
export default EditProduct;
This React EditProduct component allows users to update product details by fetching the existing product data and submitting modifications. It uses useParams
to extract the product ID from the URL, useNavigate
to redirect after a successful update, and useState
to manage the product's name, description, and price. The useEffect
hook fetches the product details when the component mounts or when the ID changes. The form dynamically updates the state as users edit the fields. Upon submission, updateProduct
is called to send the updated data to the API, and the user is redirected to the product list page. This component ensures a smooth product editing experience in a React application.
Create the Auth Form Component (
components/AuthForm.js
)
The AuthForm.js
component is a reusable authentication form that can be used for both Login and Registration.
// Import necessary modules from React
import React, { useState } from "react";
// Define the AuthForm component, which is a reusable authentication form
const AuthForm = ({ title, fields, onSubmit, toggleText, toggleAction }) => {
// useState to store the form input values dynamically
const [formData, setFormData] = useState({});
// Function to handle changes in input fields
const handleChange = (e) => {
// Update the corresponding field in the formData state
setFormData({ ...formData, [e.target.name]: e.target.value });
};
return (
<div>
{/* Display the form title (e.g., "Login" or "Register") */}
<h2>{title}</h2>
{/* Form submission: Calls onSubmit function with formData when submitted */}
<form onSubmit={(e) => onSubmit(e, formData)}>
{/* Iterate over the fields array to dynamically generate input fields */}
{fields.map((field) => (
<div key={field.name}> {/* Use field name as unique key */}
{/* Display the label for the input field */}
<label>{field.label}:</label>
{/* Input field with dynamic type, name, and value */}
<input
// Type of input (text, email, password, etc.)
type={field.type}
// Name of the input field
name={field.name}
// Assign value from state or default to an empty string
value={formData[field.name] || ""}
// Update state when input changes
onChange={handleChange}
// Make the field mandatory
required
/>
</div>
))}
{/* Submit button for the form */}
<button type="submit">{title}</button>
</form>
{/* Toggle option for switching between login and registration */}
<p>
{toggleText} {/* Display toggle message, e.g., "Don't have an account?" */}
<button onClick={toggleAction}>Click here</button> {/* Trigger action to switch forms */}
</p>
</div>
);
};
// Export the component for use in other parts of the application
export default AuthForm;
This React AuthForm component is a reusable authentication form that dynamically handles both login and registration. It uses useState
to manage form input values and generates input fields dynamically based on the fields
prop. The handleChange
function updates the state as users type. When submitted, the form calls the onSubmit
function with the form data. A toggle button allows users to switch between login and registration by triggering the toggleAction
function. The component ensures flexibility, reusability, and dynamic rendering for authentication forms in a React application.
Create the Button Component (
components/Button.js
)
// Import React library to create a functional component
import React from "react";
// Define a reusable Button component
// Accepts three props:
// - text: The label to be displayed on the button
// - onClick: The function to execute when the button is clicked
// - type: The button type (default is "button", but can be "submit" or "reset")
const Button = ({ text, onClick, type = "button" }) => (
// Render a button element with the specified type and onClick handler
<button type={type} onClick={onClick}>
{/* Display the text inside the button */}
{text}
</button>
);
// Export the Button component for use in other parts of the application
export default Button;
This React Button component is a reusable UI element that accepts three props: text
for the button label, onClick
for handling click events, and type
to specify the button type (button
, submit
, or reset
, with button
as the default). It renders a <button>
element with the given properties, making it flexible for various actions in a React application.
Create the Loading Component (
components/loading.js
)
// Import the React library to create a functional component
import React from "react";
// Define the Loading component
// This component displays a "Loading..." message to indicate that data is being loaded
const Loading = () => {
return (
// Render a paragraph element with inline styles for alignment and appearance
<p style={{ textAlign: "center", fontSize: "18px", fontWeight: "bold" }}>
Loading... {/* Display the loading text */}
</p>
);
};
// Export the Loading component for use in other parts of the application
export default Loading;
The React Loading component is a simple UI element that displays a "Loading..." message while data is being fetched or processed. It returns a <p>
element with centered, bold, and slightly larger text to visually indicate the loading state, enhancing the user experience by providing feedback during asynchronous operations.
Create the Navigation Bar Component (
components/navbar.js
)
// Import the React library to create a functional component
import React from "react";
// Import Link for navigation and useNavigate for programmatic redirection
import { Link, useNavigate } from "react-router-dom";
// Define the Navbar component
// Accepts an optional `onLogout` prop for handling logout functionality externally
const Navbar = ({ onLogout }) => {
// Initialize navigate to enable redirection after logout
const navigate = useNavigate();
// Function to handle user logout
const handleLogout = () => {
console.log("Logout triggered in Navbar"); // Debugging: Log message when logout is triggered
// Remove authentication token from local storage
localStorage.removeItem("token");
// Remove user ID from local storage
localStorage.removeItem("user_id");
// If an `onLogout` function is provided as a prop, call it
if (onLogout) onLogout();
// Redirect the user to the home page (or login page)
navigate("/");
};
return (
// Navigation bar container with inline styles for layout and styling
<nav
style={{
display: "flex", // Use flexbox for layout
justifyContent: "space-between", // Space items evenly
padding: "10px", // Add padding for spacing
borderBottom: "1px solid #ccc" // Add a bottom border for separation
}}
>
{/* Brand/logo section */}
<h3>My Store</h3>
{/* Navigation links section */}
<div>
{/* Link to the products page */}
<Link to="/products" style={{ marginRight: "10px" }}>Products</Link>
{/* Link to create a new product */}
<Link to="/create-product" style={{ marginRight: "10px" }}>Create Product</Link>
{/* Logout button to trigger the handleLogout function */}
<button onClick={handleLogout}>Logout</button>
</div>
</nav>
);
};
// Export the Navbar component for use in other parts of the application
export default Navbar;
The React Navbar component provides navigation links for the application and includes a logout function. It displays links to the products page and the product creation page using Link
from React Router and uses useNavigate
for redirecting after logout. When the logout button is clicked, it removes authentication data from local storage, optionally triggers an external onLogout
function if provided, and redirects the user to the home or login page. The component is styled with flexbox for layout and includes a bottom border for visual separation, ensuring a clean and user-friendly navigation bar in a React application.
Create the Product Card Component (
components/ProductCard.js
)
// Import the React library to create a functional component
import React from "react";
// Import Link from React Router for navigation
import { Link } from "react-router-dom";
// Import the API function to delete a product
import { deleteProduct } from "../services/api";
// Define the ProductCard component
// Accepts two props:
// - `product`: The product object containing details like name, description, and price
// - `onDelete`: A function to update the UI after a product is deleted
const ProductCard = ({ product, onDelete }) => {
// Function to handle product deletion
const handleDelete = async () => {
// Display a confirmation popup before deleting the product
if (window.confirm("Are you sure you want to delete this product?")) {
try {
// Call the API function to delete the product using its ID
await deleteProduct(product.id);
// Call the `onDelete` function passed from the parent component to update the UI
onDelete(product.id);
} catch (error) {
// Log the error to the console
console.error("Error deleting product:", error);
// Show an error message to the user
alert("An error occurred while deleting the product. Please try again.");
}
}
};
return (
// Container div for the product card with inline styles
<div
style={{
border: "1px solid #ddd", // Light border around the card
padding: "10px", // Padding inside the card
margin: "10px", // Spacing between product cards
borderRadius: "5px" // Rounded corners for a smooth look
}}
>
{/* Display product name */}
<h3>{product.name}</h3>
{/* Display product description */}
<p>{product.description}</p>
{/* Display product price in bold */}
<p><strong>Price:</strong> ${product.price}</p>
{/* Link to the edit page for this product */}
<Link to={`/edit-product/${product.id}`} style={{ marginRight: "10px" }}>
Edit
</Link>
{/* Delete button - Calls handleDelete function when clicked */}
<button onClick={handleDelete} style={{ color: "red" }}>
Delete
</button>
</div>
);
};
// Export the ProductCard component for use in other parts of the application
export default ProductCard;
The React ProductCard component displays product details and provides options to edit or delete a product. It accepts product
as a prop to show the name, description, and price, and onDelete
to update the UI after deletion. The handleDelete
function prompts for confirmation before calling the deleteProduct
API function and removes the item from the list if successful. An edit link navigates to the product's edit page, and the delete button triggers product removal with an error-handling alert. The component is styled with borders, padding, and rounded corners to maintain a clean, structured design within the application.
Conclusion
The project successfully integrates React, Django, and MySQL to build a basic application with user authentication and product management. Key accomplishments include:
User authentication with token-based login.
CRUD operations for product management.
API communication using Axios.
Client-side routing with React Router.
Component-based structure for maintainability.
Centralized API service for consistency.
Opportunities for Improvement:
Enhanced error handling for API requests and UI feedback.
Better state management using React Query or Redux.
Improve User Interface with a CSS Framework or a beautiful and responsive CSS settings.
Security enhancements such as input validation and secure token storage
Thanks!
Subscribe to my newsletter
Read articles from Leo Bcheche directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
