为什么我无法访问从 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...</>;
}
我正在尝试使用 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...</>;
}