You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React项目中如何用真实API数据替换UUID生成的模拟数据并保留productCode作为标识符

替换模拟数据为API数据的完整解决方案

没问题,我来帮你把API数据无缝替换掉原来的模拟数据,同时保持现有功能完全正常。核心思路是把API返回的嵌套数据结构转换成你现有组件期望的格式——因为你的组件已经依赖了productCode、value、vendor这些字段,我们只需要做一层数据映射,不用改动组件核心逻辑。

步骤1:替换数据获取逻辑,转换API数据格式

首先,删掉原来的data模拟数据生成代码,把useEffect里设置products的逻辑换成API请求,同时对返回的数据做格式转换:

export default function App() {
  const [products, setProducts] = useState([]);
  const [cart, setCart] = useState([]);

  // 替换模拟数据逻辑,改用API获取并转换格式
  useEffect(() => {
    fetch("http://localhost:1337/api/rows/")
      .then((response) => response.json())
      .then((apiResponse) => {
        // 将API返回的嵌套结构转换成组件需要的扁平格式
        const transformedProducts = apiResponse.data.map(item => ({
          productCode: item.attributes.ProductCode, // 对应API的ProductCode
          value: item.attributes.Description,       // 用API的Description作为商品描述
          vendor: item.attributes.Vendor,           // 用API的Vendor作为供应商
          price: item.attributes.DefaultCost,       // 用API的DefaultCost作为商品价格
          id: item.id                               // 保留API返回的原始id(可选)
        }));
        setProducts(transformedProducts);
      })
      .catch(error => console.error("获取产品数据失败:", error));
  }, []);

  // 下面的handleClick、handleChange逻辑完全保留,不用修改
  // ... 原有代码
}

这里的关键是字段精准映射,确保转换后的数据和原来模拟数据的字段名、含义完全一致,这样所有依赖这些字段的组件都能正常工作。

步骤2:修复路由参数的类型匹配问题

API返回的ProductCode是数字类型,但路由参数默认是字符串,所以在ItemDetail组件里需要把参数转成数字才能正确匹配商品:

function ItemDetail({ handleClick, products }) {
  const { productCode, value, vendor } = useParams();
  // 把字符串类型的路由参数转成数字,匹配API返回的productCode类型
  const targetProduct = products.find(item => item.productCode === Number(productCode));
  
  return (
    <div>
      <p>product id: {targetProduct.productCode}</p>
      <p>Description: {targetProduct.value}</p>
      <p>vendor: {targetProduct.vendor}</p>
      <button onClick={() => handleClick(targetProduct)}>Add to Cart</button>
    </div>
  );
}

另外,如果API返回的商品描述或供应商名称包含空格、特殊字符,建议对路由参数做编码处理,避免路由解析出错:

const Products = ({ products }) => {
  return (
    <>
      <h1>Products</h1>
      <ul>
        {products.map((item) => (
          <li key={item.productCode}>
            <Link 
              to={generatePath(
                "/itemDetail/:productCode/:value/:vendor", 
                {
                  productCode: item.productCode,
                  value: encodeURIComponent(item.value),
                  vendor: encodeURIComponent(item.vendor)
                }
              )} 
            >
              {item.value}
            </Link>
          </li>
        ))}
      </ul>
    </>
  );
};

步骤3:确认购物车逻辑正常运行

你的购物车逻辑已经用productCode作为唯一标识符,转换后的数据里productCode是业务唯一的(API返回的每个商品ProductCode应该是唯一的),所以handleClick和handleChange完全不需要修改。

唯一的小优化:把Cart组件里的key从item.id改成item.productCode,因为它是业务上的唯一标识,更符合React key的使用规范:

const Cart = ({ cart, setCart, handleChange }) => {
  // ... 原有逻辑
  return (
    <article>
      {cart.map((item) => (
        <div className="cart_box" key={item.productCode}> {/* 改用productCode作为key */}
          {/* ... 原有内容 */}
        </div>
      ))}
      {/* ... 原有内容 */}
    </article>
  );
};

完整修改后的App代码

这里整合了所有修改的完整代码:

import "./styles.css";
import { useEffect, useState } from "react";
import { BrowserRouter as Router, Routes, Route, useParams, Link, generatePath } from "react-router-dom";

const Home = () => <h1>Home</h1>;

function ItemDetail({ handleClick, products }) {
  const { productCode, value, vendor } = useParams();
  const targetProduct = products.find(item => item.productCode === Number(productCode));
  
  return (
    <div>
      <p>product id: {targetProduct.productCode}</p>
      <p>Description: {targetProduct.value}</p>
      <p>vendor: {targetProduct.vendor}</p>
      <button onClick={() => handleClick(targetProduct)}>Add to Cart</button>
    </div>
  );
}

const Cart = ({ cart, setCart, handleChange }) => {
  const handleRemove = (productCode) => {
    setCart((cart) => cart.filter((item) => item.productCode !== productCode));
  };
  const price = cart.reduce(
    (total, item) => total + item.amount * item.price,
    0
  );
  return (
    <article>
      {cart.map((item) => (
        <div className="cart_box" key={item.productCode}>
          <p>{item.value}</p>
          <div>
            <button onClick={() => handleChange(item.productCode, 1)}>+</button>
            <button>{item.amount}</button>
            <button onClick={() => handleChange(item.productCode, -1)}> - </button>
          </div>
          <div>
            <span>{item.price}</span>
            <button onClick={() => handleRemove(item.productCode)}> Remove </button>
          </div>
        </div>
      ))}
      <div className="total">
        <span>Total Price of your Cart</span>
        <span>R - {price}</span>
      </div>
    </article>
  );
};

const Products = ({ products }) => {
  return (
    <>
      <h1>Products</h1>
      <ul>
        {products.map((item) => (
          <li key={item.productCode}>
            <Link 
              to={generatePath(
                "/itemDetail/:productCode/:value/:vendor", 
                {
                  productCode: item.productCode,
                  value: encodeURIComponent(item.value),
                  vendor: encodeURIComponent(item.vendor)
                }
              )} 
            >
              {item.value}
            </Link>
          </li>
        ))}
      </ul>
    </>
  );
};

export default function App() {
  const [products, setProducts] = useState([]);
  const [cart, setCart] = useState([]);

  useEffect(() => {
    fetch("http://localhost:1337/api/rows/")
      .then((response) => response.json())
      .then((apiResponse) => {
        const transformedProducts = apiResponse.data.map(item => ({
          productCode: item.attributes.ProductCode,
          value: item.attributes.Description,
          vendor: item.attributes.Vendor,
          price: item.attributes.DefaultCost,
          id: item.id
        }));
        setProducts(transformedProducts);
      })
      .catch(error => console.error("Failed to fetch products:", error));
  }, []);

  const handleClick = (item) => {
    if (cart.some((cartItem) => cartItem.productCode === item.productCode)) {
      setCart((cart) => cart.map((cartItem) => cartItem.productCode === item.productCode
        ? { ...cartItem, amount: cartItem.amount + 1 }
        : cartItem
      ));
      return;
    }
    setCart((cart) => [
      ...cart,
      { ...item, amount: 1 }
    ]);
  };

  const handleChange = (productCode, d) => {
    setCart((cart) => cart.flatMap((cartItem) => cartItem.productCode === productCode
      ? cartItem.amount + d < 1
        ? []
        : [ { ...cartItem, amount: cartItem.amount + d } ]
      : [cartItem]
    ));
  };

  return (
    <div className="App">
      <Router>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/products">Products</Link>
          </li>
          <li>
            <Link to="/cart">Cart</Link>
          </li>
        </ul>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="products" element={<Products products={products} />} />
          <Route path="/itemDetail/:productCode/:value/:vendor" element={
            <ItemDetail products={products} handleClick={handleClick} />
          } />
          <Route path="/Cart/" element={
            <Cart cart={cart} setCart={setCart} handleChange={handleChange} />
          } />
        </Routes>
      </Router>
    </div>
  );
}

额外注意事项

  • 错误处理:API请求添加了catch分支,方便调试请求失败的情况;
  • 价格格式化:如果API返回的DefaultCost是整数,可以用item.price.toFixed(2)把价格格式化为两位小数,提升显示体验;
  • 加载状态:可以添加一个加载状态组件,在API请求未完成时显示加载提示,避免页面空白。

这样修改后,你的应用就能完全使用API数据,同时原有功能(商品列表、详情、购物车加减/删除、总价计算)都能正常运行。

内容的提问来源于stack exchange,提问作者Manasseh Codes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 20:52:31