为什么我无法访问从 express 中的 API 获取的 JSON 对象的子属性

Why can't I access the sub-properties of a JSON object fetched from my API in express

我正在尝试使用 Axios 通过 React Hooks 将一个名为 products 的对象从我用 express 创建的简单 API 带到我的前端,并且显然,只要我不尝试访问主对象中对象内部的属性,一切都可以正常工作,因为那是当我尝试这个时,React 会抛出以下错误:

TypeError: Cannot read property '0' of undefined

ProductScreen
src/screens/ProductScreen.js:40

  37 |     text={`${product.numReviews} reviews`}
  38 |   />
  39 | </ListGroup.Item>
> 40 | <ListGroup.Item>Precio: ${product.type[0].price}</ListGroup.Item>
     | ^  41 | <ListGroup.Item>
  42 |   Descripción: <br /> {product.description}
  43 | </ListGroup.Item>

View compiled

▶ 16 stack frames were collapsed.

如果我不访问 product.type [0] .price,它是主对象中的一个对象,所有内容都会正确呈现。

以下是 React 中出现问题的组件代码:

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { Row, Col, Image, ListGroup, Card, Button } from 'react-bootstrap';
import Rating from '../components/Rating';
import axios from 'axios';

const ProductScreen = ({ match }) => {
  const [product, setProduct] = useState({});

  useEffect(() => {
    const fetchProduct = async () => {
      const product = await axios.get(`/api/products/${match.params.id}`);

      setProduct(product.data);
    };

    fetchProduct();
  }, [match]);

  return (
    <>
      <Link className='btn btn-dark my-3' to='/'>
        Volver
      </Link>
      <Row>
        <Col md={6}>
          <Image src={product.image} alt={product.name} fluid />
        </Col>
        <Col md={3}>
          <ListGroup varian='flush'>
            <ListGroup.Item>
              <h3>{product.name}</h3>
            </ListGroup.Item>
            <ListGroup.Item>
              <Rating
                value={product.rating}
                text={`${product.numReviews} reviews`}
              />
            </ListGroup.Item>
            <ListGroup.Item>Precio: ${product.type[0].price}</ListGroup.Item>
            <ListGroup.Item>
              Descripción: <br /> {product.description}
            </ListGroup.Item>
          </ListGroup>
        </Col>
        <Col>
          <Card>
            <ListGroup variant='flush'>
              <ListGroup.Item>
                <Row>
                  <Col>Precio:</Col>
                  <Col>
                    <strong>$</strong>
                  </Col>
                </Row>
              </ListGroup.Item>
              <ListGroup.Item>
                <Row>
                  <Col>Status:</Col>
                  <Col></Col>
                </Row>
              </ListGroup.Item>
              <ListGroup.Item>
                <Button className='btn-block' type='button'>
                  Agregar al carrito
                </Button>
              </ListGroup.Item>
            </ListGroup>
          </Card>
        </Col>
      </Row>
    </>
  );
};

export default ProductScreen;

这里可以看出我是用React Hooks把后台的信息带过来的,具体执行这个任务的代码行如下:

  const [product, setProduct] = useState({});

  useEffect(() => {
    const fetchProduct = async () => {
      const product = await axios.get(`/api/products/${match.params.id}`);

      setProduct(product.data);
    };

    fetchProduct();
  }, [match]);

product 变量中保存 JSON 对象


我认为问题的一个关键点是,如果我在浏览器开发人员工具中转到网络,获取对象的 Header 会给我:

 - Content-Type: text/html; charset = UTF-8 
 - Status Code: 200 
 - Response:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <link rel="icon" href="/favicon.ico" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta name="theme-color" content="#000000" />
    <meta name="description" content="El sabor del norte a tu casa" />
    <link rel="apple-touch-icon" href="/logo192.png" />
    <link rel="manifest" href="/manifest.json" />
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.1/css/all.min.css"
      integrity="sha512-+4zCK9k+qNFUR5X+cKL9EIR+ZOhtIloNl9GIKS57V1MyNsYpYcUrUeQc9vNfzsWfV28IaLL3i96P9sdNyeRssA=="
      crossorigin="anonymous"
    />
    <title>Bienvenido a Grill Shop</title>
  </head>
  <body>
    <noscript>You need to enable JavaScript to run this app.</noscript>
    <div id="root"></div>
  <script src="/static/js/bundle.js"></script><script src="/static/js/0.chunk.js"></script><script src="/static/js/main.chunk.js"></script><script src="/main.c767f2926d55ce283409.hot-update.js"></script></body>
</html>

我们可以清楚地看到它没有给我任何 JSON 对象,这解释了为什么当我尝试访问对象的属性时它抛出未定义的错误

我已经检查了我的服务器文件和后端对前端发出的 get 请求的响应,准确地说响应是一个 JSON 对象 这就是为什么我不明白为什么当试图将这个对象带到前端时,服务器的响应是一个 HTML / Text object

以下是我的 API - express 服务器的代码:

const express = require('express');
const products = require('./data/products');

const app = express();

app.get('/', (req, res) => {
  res.send('API is running...');
});

app.get('/api/products', (req, res) => {
  res.json(products);
});

app.get('/api/products/:id', (req, res) => {
  const product = products.find((p) => p._id === req.params.id);
  res.json(product);
});

app.listen(5000, console.log('Server running on port 5000'));

下图显示了我从后端得到的响应:

如您所见,我的 API 在端口 5000 上是 运行,而我的前端在端口 3000 上。在我的前端配置文件中,我添加了一个指向端口 5000 的代理,以便能够访问后端服务

先谢谢你了,因为我花了几天时间试图解决这个问题,但我还没有找到解决方案

您没有告诉前端在查询后端时使用端口 5000。

这一行:

const product = await axios.get(`/api/products/${match.params.id}`)

将对来自同一主机 (localhost:3000) 执行请求,因此整个 url 将是:

http://localhost:3000/api/products/${match.params.id}

只需设置正确的主机即可:

const product = await axios.get(BASE_URL + `/api/products/${match.params.id}`)

其中 BASE_URL 应该是一个全局常量,您可以根据部署后端的位置进行相应更改,并且在本地计算机上开发时,应该是 http://localhost:5000

您使用空对象作为默认值 (useState({}))。当组件安装时,它会发出您的网络请求,但是响应需要一些时间才能通过。在此期间,默认参数 {} 用作 product。因此 product.type 和您访问的所有其他属性将 return undefined.

使用 React 时,您必须始终明确指定要渲染的内容,即使这意味着什么都不应该渲染。

一个简单的解决方案是使用默认值 null,并且仅在 product 为真时才渲染某些内容。

const [product, setProduct] = useState(null);

useEffect(() => {
  const fetchProduct = async () => {
    const product = await axios.get(`/api/products/${match.params.id}`);
    setProduct(product.data);
  };

  fetchProduct();
}, [match]);

// render nothing during the time product is loading
if (!product) return null;

return <>render product</>;

或者,您也可以选择呈现某些内容以表明 product 正在加载。

// assuming you use null as default
if (product) {
  // product is loaded
  return <>render product</>;
} else {
  // product is loading
  return <>loading...</>;
}