使用 React 构建交互式购物车应用

在这篇文章中,我们将深入探讨如何使用 React 构建一个经典的项目——“极客购物车”。虽然这看起来是一个基础的教程项目,但作为经验丰富的开发者,我们知道,即使在 2026 年,掌握核心的状态管理和组件化逻辑依然是构建复杂 AI 原生应用的基石。我们将这个项目视为一个打磨基础的最佳实践,不仅关注功能实现,更融入了现代工程化的理念。

为什么我们要在 2026 年重写这个经典项目?

你可能会问,购物车教程不是满大街都是吗?确实如此。但在我们最近的团队复盘中,我们发现很多初级开发者在追求新技术(如 SSR 微前端或复杂的 AI Agent)时,往往忽略了 React 最本质的 单向数据流状态复用 逻辑。通过这个项目,我们希望展示如何像编写企业级代码一样编写一个简单的应用,包括关注点分离、可维护的 CSS 架构以及清晰的组件职责划分。

让我们来看看最终项目的样子。这不仅仅是一个 UI,它包含了完整的增删改查(CRUD)闭环逻辑。

!<a href="https://media.geeksforgeeks.org/wp-content/uploads/20240730125642/IMAGE1.jpg">IMAGE1

技术栈与 2026 年的开发环境:

在 2026 年,我们不再使用 create-react-app,因为它已不再维护且效率低下。在这个项目中,我们将采用更现代的工具链:

  • React 19+: 利用最新的 Compiler 特性(虽然我们这里主要编写函数组件)。
  • Vite: 下一代前端构建工具,提供极速的冷启动和热更新(HMR)。
  • CSS Modules / Scoped CSS: 为了避免全局样式污染,我们将演示如何优雅地组织样式。

项目结构设计:工程化的起点

在我们开始编写代码之前,让我们思考一下目录结构。一个好的结构能让代码具有自解释性。我们将按照功能模块划分文件,而不是简单的类型划分。

gfg-shopping-cart/
├── public/
├── src/
│   ├── components/
│   │   ├── SearchComponent.jsx
│   │   ├── ShowCourseComponent.jsx
│   │   └── UserCartComponent.jsx
│   ├── App.jsx        # 主容器与状态管理核心
│   ├── App.css        # 全局样式
│   └── main.jsx       # 入口文件
├── package.json
└── vite.config.js

核心实现逻辑:深入状态管理

在这个应用中,我们选择将所有状态(课程列表、购物车内容、搜索关键词)提升到 App.jsx 中。这种“单一数据源”的策略让我们能够更容易地实现诸如“实时计算总金额”和“搜索过滤”等跨组件功能。

#### 1. App.jsx: 应用的大脑

这是整个应用的逻辑核心。我们不仅定义了数据,还定义了操作数据的“动作”(Actions)。注意看我们如何处理 addCourseToCart 函数,它包含了防止重复添加的业务逻辑,这是我们在生产环境中经常遇到的边界情况。

// src/App.jsx
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: ‘React JS 基础‘, price: 999, image: ‘...‘ },
    { id: 2, name: ‘Node.js 高级实战‘, price: 1299, image: ‘...‘ },
    { id: 3, name: ‘Vue3 全栈开发‘, price: 899, image: ‘...‘ },
    // ... 更多课程数据
  ]);

  const [cartCourses, setCartCourses] = useState([]);
  const [searchCourse, setSearchCourse] = useState(‘‘);

  // 添加课程到购物车的逻辑
  const addCourseToCart = (course) => {
    // 检查课程是否已在购物车中
    const isAlreadyAdded = cartCourses.find(
      (item) => item.id === course.id
    );
    
    if (isAlreadyAdded) {
      // 如果已存在,我们可以选择提示用户或增加数量
      // 这里为了简单,我们不做重复添加
      return;
    }

    // 更新购物车状态
    setCartCourses([...cartCourses, course]);
  };

  // 移除课程的逻辑
  const removeCourse = (course) => {
    const updatedCart = cartCourses.filter(
      (item) => item.id !== course.id
    );
    setCartCourses(updatedCart);
  };

  // 计算总价格
  const calculateTotalAmount = () => {
    return cartCourses.reduce((total, course) => total + course.price, 0);
  };

  // 购买按钮点击处理
  const handlePurchase = () => {
    if (cartCourses.length === 0) {
      alert(‘购物车是空的,无法结账!‘);
      return;
    }
    alert(`购买成功!总金额:${calculateTotalAmount()}`);
    setCartCourses([]); // 清空购物车
  };

  return (
    

极客购物车

{/* 搜索组件 */}
{/* 商品展示组件 */} course.name.toLowerCase().includes(searchCourse.toLowerCase()) )} addCourseToCart={addCourseToCart} /> {/* 购物车组件 */}
); } export default App;

#### 2. 展示组件:解耦 UI 与 逻辑

在 2026 年,我们强调组件的“哑组件”属性。它们只负责渲染 UI,通过 Props 接收数据和回调函数。

ShowCourseComponent.jsx:

// src/components/ShowCourseComponent.jsx
import React from ‘react‘;

function ShowCourseComponent({ 
  courses, // 原始数据(未使用但保留作为示例)
  filteredCourses, // 过滤后的数据
  addCourseToCart // 回调函数
}) {
  return (
    
{/* 这里我们可以优化为按列显示 */} {filteredCourses.map((course) => (
{course.name} Rs {course.price}
))} {filteredCourses.length === 0 && (

没有找到匹配的课程。

)}
); } export default ShowCourseComponent;

UserCartComponent.jsx:

// src/components/UserCartComponent.jsx
import React from ‘react‘;

function UserCartComponent({ 
  cartCourses, 
  removeCourse, 
  totalAmount, 
  handlePurchase 
}) {
  return (
    

你的购物车

{cartCourses.length === 0 ? (

购物车是空的。

) : (
{cartCourses.map((course) => (
{course.name} Rs {course.price}
))}

总金额: Rs {totalAmount}

)}
); } export default UserCartComponent;

#### 3. 现代化样式:CSS 变量与 Flexbox

虽然我们这里在一个 CSS 文件中编写样式,但在实际的大型项目中,我们建议使用 CSS-in-JS (如 styled-components) 或 Tailwind CSS。为了保持本教程的纯粹性,我们使用原生 CSS,但采用了现代化的属性。

/* App.css - 补充完整的样式定义 */

/* 布局优化 */
.App-main {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.product-list {
  display: grid;
  grid-template-columns: 2fr 1fr; /* 左侧商品,右侧购物车 */
  gap: 30px;
  margin-top: 20px;
}

/* 响应式设计:在移动端堆叠显示 */
@media (max-width: 768px) {
  .product-list {
    grid-template-columns: 1fr;
  }
}

/* 商品卡片样式 */
.course-container {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 15px;
}

.product-card {
  background-color: rgb(255, 245, 245);
  border: 1px solid #ddd;
  border-radius: 8px;
  padding: 15px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  box-shadow: 0 2px 5px rgba(0,0,0,0.05);
  transition: transform 0.2s;
}

.product-card:hover {
  transform: translateY(-5px);
}

.add-to-cart-btn {
  background-color: #6cc24a;
  color: white;
  border: none;
  padding: 10px;
  border-radius: 4px;
  cursor: pointer;
  margin-top: 10px;
}

.add-to-cart-btn:hover {
  background-color: #5aae38;
}

/* 购物车样式 */
.cart-container {
  background-color: #fff;
  border: 1px solid #eee;
  border-radius: 8px;
  padding: 20px;
  height: fit-content;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.cart-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid #f0f0f0;
}

.checkout-btn {
  width: 100%;
  background-color: #333;
  color: white;
  padding: 12px;
  border: none;
  border-radius: 4px;
  font-size: 16px;
  cursor: pointer;
}

2026 开发视角:技术债务与未来优化

作为一名经验丰富的开发者,我必须指出,上述代码虽然逻辑清晰,但在现代大型生产环境中还存在局限性。让我们来探讨一下如果我们要把这个应用推向市场,还需要考虑什么。

1. 状态管理的演进:从 useState 到 Zustand/Redux

在这个简单的例子中,使用 useState 进行“Prop Drilling”(属性透传)是可以接受的。但想象一下,如果我们有 20 个组件都需要访问购物车数据?

在 2026 年,我们可能会选择 ZustandJotai 这样的轻量级状态管理库。它们摒弃了 Redux 的样板代码,提供了更直观的 API。例如,我们可以创建一个 useCartStore,任何组件都可以直接读取和修改购物车,而不需要层层传递 Props。

2. 性能优化的深水区:useMemo 与 Virtualization

你有没有想过,如果我们的课程列表有 10,000 条数据,单纯的 map 渲染会导致页面卡顿?

我们通常会引入 React VirtualTanStack Virtual 这样的库,只渲染可视区域内的 DOM 节点。此外,对于 INLINECODEdd1071d3 和 INLINECODE96fdb5d4 的计算,务必使用 useMemo 进行缓存,避免在每次组件重渲染时都重新进行昂贵的计算或过滤操作。

3. AI 辅助开发实战:我们是如何写的

在编写这段代码时,我们利用了 AI 辅助工具(如 Cursor 或 GitHub Copilot)。

  • 生成组件骨架: 我们输入了 // Create a functional component named UserCartComponent that accepts cartCourses and removeCourse,AI 瞬间生成了组件结构。
  • CSS 调优: 我们让 AI “把这段 CSS 改成 Flexbox 布局并添加 hover 效果”,它迅速给出了现代化的样式代码。

这不仅是代码生成,更是“结对编程”的体现。AI 帮助我们处理了繁琐的语法工作,让我们专注于业务逻辑——即“如何让购物车体验更好”。

总结

通过构建这个“极客购物车”,我们不仅复习了 React 的核心概念,更重要的是,我们以 2026 年的视角重新审视了基础架构的重要性。无论是处理边缘情况(如重复添加商品),还是考虑到未来的扩展性(CSS 模块化、状态管理),都是我们通往高级开发者的必经之路。

我们鼓励你在这个基础上进行扩展:尝试接入一个真实的支付 API(如 Stripe),或者使用 LocalStorage 持久化购物车数据,甚至使用 React Query 来管理服务器状态。在这个过程中,你会遇到各种 Bug,但这正是学习的最佳时机。

在下一篇文章中,我们将探讨如何将此应用部署到 Vercel 或 Netlify 的 Edge Network 上,实现全球秒开。祝你编码愉快!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/28124.html
点赞
0.00 平均评分 (0% 分数) - 0