使用React的购物车应用
在本文中,我们将使用著名的JavaScript前端库ReactJS创建一个交互式和响应式购物车项目应用。我们将应用命名为 “GeeksforGeeks购物车”。开发的项目主要关注功能组件,同时管理应用的各个状态。该项目为用户提供浏览不同产品或商品、将商品添加到购物车以及进行购买的功能。用户还可以实时查看购买金额,并能够高效地删除添加到购物车的产品。
让我们动态地看一下我们的最终项目将是什么样子:
使用的技术和先决条件:
- ReactJS
- CSS
- JSX
- React中的函数组件
途径:
给定的代码展示了一个简单但功能完整的购物车项目,我们将其命名为“ GeeksforGeeks购物车 “。该项目完全使用ReactJS开发,并在CSS中应用样式。开发的界面允许用户执行Amazon购物车和Flipkart购物车提供的不同活动,例如浏览课程,将该课程添加到购物车,删除课程,查看实时购买金额,并投影到结账页面。在该应用程序中可以轻松集成不同的功能,例如添加付款网关或添加更多产品或课程。
项目结构:
package.json中的依赖关系将如下所示:
{
"name": "gfg-shopping-cart",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
创建应用的步骤:
步骤1: 在VSCode IDE中使用以下命令设置React项目。
npx create-react-app <<name of project>>
步骤2: 通过执行下面的命令,转到新创建的项目文件夹。
cd <<Name_of_project>>
步骤3: 创建components文件夹并创建文件SearchComponent.js、ShowCourseComponent.js和UserCartComponent.js
示例: 在上述目录结构中的App.js和App.css文件中插入以下代码。
- App.js: 该文件代表购物车应用程序的主要组件,负责管理状态和处理添加课程到购物车、删除、管理数量和结账等所有逻辑。
- App.css: 该文件包含所有样式代码,无论是h1标签的样式设置还是购物车为空消息的样式设置。整个应用程序的外观和感觉都在这个样式文件中指定。
- SearchComponent.jsx: 该文件代码包含了搜索栏的逻辑,用户可以从中搜索指定的课程。
- ShowCourseComponent.jsx: 该文件包含了根据用户输入显示匹配课程的代码。简而言之,课程显示的卡片是从这个文件中呈现的。
- UserCartComponent.jsx: 该文件负责用户购物车的逻辑。在这里,通过使用不同的props,可以删除已添加的课程,用户可以选择数量并执行各种功能。
//App.js
import React, { useState } from 'react';
import './App.css';
import SearchComponent from './components/SearchComponent';
import ShowCourseComponent from './components/ShowCourseComponent';
import UserCartComponent from './components/UserCartComponent';
function App() {
const [courses, setCourses] = useState([
{ id: 1,
name: 'GFG T-shirt',
price: 499,
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20230823165506/gfg1.png'
},
{ id: 2,
name: 'GFG Bag',
price: 699,
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20230823165553/gfg2.jpg'
},
{ id: 3,
name: 'GFG Hoodie',
price: 799,
image:
'https://media.geeksforgeeks.org/wp-content/uploads/20230823165623/gfg3.jpg'
}
]);
const [cartCourses, setCartCourses] = useState([]);
const [searchCourse, setSearchCourse] = useState('');
const addCourseToCartFunction = (GFGcourse) => {
const alreadyCourses = cartCourses
.find(item => item.product.id === GFGcourse.id);
if (alreadyCourses) {
const latestCartUpdate = cartCourses.map(item =>
item.product.id === GFGcourse.id ? {
...item, quantity: item.quantity + 1 }
: item
);
setCartCourses(latestCartUpdate);
} else {
setCartCourses([...cartCourses, {product: GFGcourse, quantity: 1}]);
}
};
const deleteCourseFromCartFunction = (GFGCourse) => {
const updatedCart = cartCourses
.filter(item => item.product.id !== GFGCourse.id);
setCartCourses(updatedCart);
};
const totalAmountCalculationFunction = () => {
return cartCourses
.reduce((total, item) =>
total + item.product.price * item.quantity, 0);
};
const courseSearchUserFunction = (event) => {
setSearchCourse(event.target.value);
};
const filterCourseFunction = courses.filter((course) =>
course.name.toLowerCase().includes(searchCourse.toLowerCase())
);
return (
<div className="App">
<SearchComponent searchCourse={searchCourse}
courseSearchUserFunction=
{courseSearchUserFunction} />
<main className="App-main">
<ShowCourseComponent
courses={courses}
filterCourseFunction={filterCourseFunction}
addCourseToCartFunction={addCourseToCartFunction}
/>
<UserCartComponent
cartCourses={cartCourses}
deleteCourseFromCartFunction={deleteCourseFromCartFunction}
totalAmountCalculationFunction={
totalAmountCalculationFunction
}
setCartCourses={setCartCourses}
/>
</main>
</div>
);
}
export default App;
Javascript
//components/SearchComponent.js
import React from 'react';
function SearchComponent({ searchCourse, courseSearchUserFunction }) {
return (
<header className="App-header">
<h1>GeeksforGeeks Shopping Cart</h1>
<div className="search-bar">
<input
type="text"
placeholder="Search for GFG Products..."
value={searchCourse}
onChange={courseSearchUserFunction}
/>
</div>
</header>
);
}
export default SearchComponent;
JavaScript
//components/ShowCourseComponent.js
import React from 'react';
function ShowCourseComponent({ courses,
filterCourseFunction,
addCourseToCartFunction }) {
return (
<div className="product-list">
{filterCourseFunction.length === 0 ? (
<p className="no-results">
Sorry Geek, No matching Product found.
</p>
) : (
filterCourseFunction.map((product) => (
<div className="product" key={product.id}>
<img src={product.image} alt={product.name} />
<h2>{product.name}</h2>
<p>Price: ₹{product.price}</p>
<button
className="add-to-cart-button"
onClick={() => addCourseToCartFunction(product)}
>
Add to Shopping Cart
</button>
</div>
))
)}
</div>
);
}
export default ShowCourseComponent;
JavaScript
//components/UserCartComponent.js
import React from 'react';
function UserCartComponent({
cartCourses,
deleteCourseFromCartFunction,
totalAmountCalculationFunction,
setCartCourses,
}) {
return (
<div className={`cart ${cartCourses.length > 0 ? 'active' : ''}`}>
<h2>My Cart</h2>
{cartCourses.length === 0 ? (
<p className="empty-cart">Geek, your cart is empty.</p>
) : (
<div>
<ul>
{cartCourses.map((item) => (
<li key={item.product.id} className="cart-item">
<div>
<div className="item-info">
<div className="item-image">
<img src={item.product.image}
alt={item.product.name} />
</div>
<div className="item-details">
<h3>{item.product.name}</h3>
Price: ₹{item.product.price}
</div>
</div>
<div>
<div className="item-actions">
<button
className="remove-button"
onClick={() =>
deleteCourseFromCartFunction(item.product)}>
Remove Product
</button>
<div className="quantity">
<button style={{ margin: "1%" }}
onClick={(e) => {
setCartCourses((prevCartCourses) => {
const updatedCart = prevCartCourses.map(
(prevItem) =>
prevItem.product.id === item.product.id
? { ...prevItem, quantity:
item.quantity + 1 }
: prevItem
);
return updatedCart;
})
}}>+</button>
<p className='quant'>{item.quantity} </p>
<button
onClick={(e) => {
setCartCourses((prevCartCourses) => {
const updatedCart = prevCartCourses.map(
(prevItem) =>
prevItem.product.id === item.product.id
? { ...prevItem, quantity:
Math.max(item.quantity - 1, 0) }
: prevItem
);
return updatedCart;
})
}}>-</button>
</div>
</div>
</div>
</div>
</li>
))}
</ul>
<div className="checkout-section">
<div className="checkout-total">
<p className="total">Total Amount:
₹{totalAmountCalculationFunction()}
</p>
</div>
<button
className="checkout-button"
disabled={cartCourses.length === 0 ||
totalAmountCalculationFunction() === 0}
>
Proceed to Payment
</button>
</div>
</div>
)}
</div>
);
}
export default UserCartComponent;
CSS
/* App.css */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
background-color: #ffffff;
}
.App-header {
background-color: #6cc24a;
padding: 20px;
color: white;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
text-align: center;
}
.App-main {
display: flex;
flex-wrap: wrap;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 20px;
}
/* Search Component */
.search-bar {
width: 100%;
max-width: 400px;
margin: 0 auto;
display: flex;
align-items: center;
background-color: white;
border-radius: 20px;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
padding: 5px 10px;
}
.search-bar input {
flex: 1;
padding: 10px;
border: none;
border-radius: 5px;
font-size: 16px;
background-color: #ffffff;
transition: background-color 0.3s ease-in-out;
}
.search-bar input:focus {
outline: none;
background-color: #ffffff;
}
.search-icon {
font-size: 1.2rem;
margin-right: 10px;
color: #6cc24a;
}
/* Product Display */
.product-list {
flex: 2;
display: flex;
gap: 20px;
}
.product {
background-color: rgb(255, 245, 245);
border: 1px solid #1b4e1f;
border-radius: 10%;
padding: 10px 60px;
text-align: center;
width: 33%;
transition: transform 0.2s ease-in-out;
cursor: pointer;
overflow: hidden;
position: relative;
}
.product:hover {
transform: translateY(-5px) scale(1.03);
}
.product img {
max-width: 150px;
/* Increased image size */
height: auto;
margin-bottom: 10px;
border-radius: 50%;
box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.2);
/* Enhanced box shadow */
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
/* Added box shadow transition */
}
.product:hover img {
transform: scale(1.1);
z-index: 1;
box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.3);
/* Enhanced box shadow on hover */
}
.product h2 {
font-size: 1.5rem;
margin: 10px 0;
color: #323754;
}
.product p {
font-size: 1.1rem;
color: #777;
margin: 5px 0;
}
/* Cart Checkout */
.cart {
flex: 1;
min-width: 300px;
margin-top: 3%;
background-color: #fff9e6;
border: 2px solid #193d10;
border-radius: 20px;
padding: 10px 20px;
box-shadow: 0px 10px 20px rgba(0, 0, 0, 0.1);
display: none;
position: sticky;
top: 20px;
}
.cart.active {
display: block;
}
.cart h2 {
font-size: 1.8rem;
color: #323754;
margin-top: 0;
text-align: center;
}
.cart ul {
list-style: none;
padding: 0;
margin: 0;
}
.cart-item {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
margin: 15px 0;
padding: 10px;
border-bottom: 1px solid #e0e0e0;
}
.cart-item .item-image img {
max-width: 90px;
height: auto;
margin-right: 20px;
border-radius: 50%;
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease-in-out;
}
.cart-item .item-image img:hover {
transform: scale(1.1);
}
.cart-item .item-details {
flex: 1;
display: inline;
}
.cart-item h3 {
display: inline;
font-size: 1.4rem;
margin: 0;
color: #323754;
}
.cart-item p {
font-size: 1.1rem;
color: #777;
margin: 5px 0;
}
.cart-item .item-actions {
display: flex;
flex-direction: row;
align-items: center;
}
.cart-item .item-actions button {
background-color: #6cc24a;
border: none;
color: white;
padding: 8px 15px;
border-radius: 20px;
cursor: pointer;
transition: background-color 0.3s ease-in-out;
font-size: 1rem;
}
.cart-item .item-actions button:hover {
background-color: #449e30;
}
.cart-item .quantity {
display: flex;
flex-direction: row;
margin-left: 15px;
font-size: 1rem;
color: #323754;
}
.cart .total {
font-size: 1.4rem;
margin: 20px 0;
text-align: right;
color: #323754;
}
.cart .checkout-button {
background-color: #6cc24a;
border: none;
color: white;
padding: 10px 15px;
border-radius: 20px;
cursor: pointer;
transition: background-color 0.3s ease-in-out;
font-size: 1.2rem;
float: left;
}
.cart .checkout-button:hover {
background-color: #449e30;
}
.checkout-message {
font-size: 1.4rem;
margin-top: 30px;
color: #6cc24a;
text-align: center;
}
.no-results,
.empty-cart {
text-align: center;
font-size: 1.4rem;
color: #777;
margin-top: 20px;
}
@media screen and (max-width: 768px) {
.App-main {
flex-direction: column;
align-items: center;
}
.product-list {
width: 100%;
margin-bottom: 5px;
}
.product {
width: 50%;
}
.cart {
min-width: unset;
margin-top: 30px;
}
}
.add-to-cart-button {
background-color: #6cc24a;
border: none;
color: white;
padding: 8px 15px;
border-radius: 20px;
cursor: pointer;
transition: background-color 0.3s ease-in-out;
font-size: 1rem;
}
.add-to-cart-button:hover {
background-color: #449e30;
}
.item-info {
display: flex;
flex-direction: row;
}
.item-details {
margin-top: 4%;
}
.item-actions .quantity p {
margin: 10% 10%;
}
.product-list {
padding-bottom: 5%;
border-bottom: 2px solid green;
}
.checkout-section {
float: right;
margin: 0;
}
.checkout-section .checkout-button {
background-color: #323754;
}
.checkout-section .checkout-button:hover {
background-color: #4c5cb6;
}
运行应用程序的步骤:
1. 在终端中执行以下命令来运行应用程序。
npm start
2. 打开像Chrome或Firefox这样的网络浏览器,在地址栏中输入以下URL。
http://localhost:3000/
输出: