我正在建立一个网上商店,前端使用ReactJS,后端使用Spree(Ruby)。
Spree提供了一种API解决方案,可以将前端和后端彼此连接。
我正在尝试显示带有产品图像的产品,但是Spree的API是通过特定方式设置的,即产品图像和产品不在同一对象中。
API响应为:
{
(holds products)data: [],
(Holds product images)included:[],
}
我的目标是创建一个显示了产品信息和产品图片的ul
。
我尝试将my API link映射到
this.state.arrays.map((product) =>
product.data
)
使用数据对象进行响应,但是我无法进行product.data.name
,因为它返回了undefined
响应
日志中的数据响应
ProductsList.js:28 PL
[undefined]
Index.js:42 productsData
{}
ProductsList.js:28 PL
[Array(5)]
0: Array(5)
0: {id: "5", type: "image", attributes: {…}}
1: {id: "4", type: "image", attributes: {…}}
2: {id: "1", type: "image", attributes: {…}}
3: {id: "3", type: "image", attributes: {…}}
4: {id: "2", type: "image", attributes: {…}}
length: 5
__proto__: Array(0)
length: 1
__proto__: Array(0)
产品索引页面
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from "prop-types";
import ProductsList from "./products/ProductsList";
import axios from 'axios';
const REACT_VERSION = React.version;
const include = '?include=images';
const API = 'https://stern-telecom-react-salman15.c9users.io/api/v2/storefront/products' + include;
const styles = {
card: {
maxWidth: 345,
},
media: {
height: 140,
},
};
class Index extends React.Component {
constructor(props){
super(props);
this.state = {
products: [],
productsData: {},
isLoading: false,
error: null,
};
}
componentDidMount() {
this.setState({ isLoading: true });
axios.get(API)
.then(result => this.setState({
products: result.data.data,
productsData: result.data,
isLoading: false,
}))
.catch(error => this.setState({
error,
isLoading: false
}));
// console.log(
// 'productsData',
// this.state.productsData
// )
}
render() {
const { products, productsData,isLoading, error } = this.state;
if (error) {
return <p>{error.message}</p>;
}
if (isLoading) {
return <p>Loading ...</p>;
}
return (
<React.Fragment>
<h1>React version: {REACT_VERSION}</h1>
<ProductsList products={this.state.productsData}/>
</React.Fragment>
);
}
}
ProductsList.propTypes = {
greeting: PropTypes.string
};
export default Index
产品列表页面
import React from "react"
import PropTypes from "prop-types"
import { withStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActionArea from '@material-ui/core/CardActionArea';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import CardMedia from '@material-ui/core/CardMedia';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
const url = "https://stern-telecom-react-salman15.c9users.io"
class ProductsList extends React.Component {
constructor(props) {
super(props);
const { products } = this.props;
const arrays = Object.values( {products} );
this.state = {
products,
arrays
};
}
render () {
return (
<React.Fragment>
<ul>
<p>Shop Products</p>
{
// console.log(
// 'PL',
// this.state.arrays.map((product) =>
// product.data
// )
// )
this.state.arrays.map(product =>
<li key={product.objectID}>
<Card>
<CardActionArea>
<CardMedia
image= {url + ''}
title={product.data.attributes.name}
/>
<CardContent>
<Typography gutterBottom variant="h5" component="h2">
{product.data.attributes.name}
</Typography>
<Typography component="p">
{product.data.attributes.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button size="small" color="primary">
{product.data.attributes.display_price}
</Button>
<Button size="small" color="primary">
add to cart
</Button>
</CardActions>
</Card>
</li>
)
}
</ul>
</React.Fragment>
);
}
}
ProductsList.propTypes = {
greeting: PropTypes.string
};
export default ProductsList
我期望得到的结果是产品信息和图片
答案 0 :(得分:4)
获取json数据后的操作错误。返回的结果是一个具有data
属性的json对象,该属性是您要传递并获取产品的数组。
您可以将products
传递给<ProductsList>
组件:
const { products, images, isLoading, error } = this.state;
...
<ProductsList products={products} images={images}/>
然后直接使用它:
class ProductsList extends React.Component {
constructor(props) {
super(props);
const { products, images } = this.props;
this.state = {
products,
images
};
...
}
...
}
或使用props.products.data
直接在ProductsList
构造函数中获取产品数组:
class ProductsList extends React.Component {
constructor(props) {
super(props);
const products = this.props.products.data;
const images = this.props.products.included;
...
}
...
}
无需使用const arrays = Object.values({ products });
,因为您已经有了包含产品的数组:
...
products: result.data.data, // products is an array with products
images: result.data.included, // images is an array with all posible images
productsData: result.data, // productsData.data is an array with products
...
此外,产品对象不包含任何名为data
的属性:
<Typography gutterBottom variant="h5" component="h2">
{product.data.attributes.name}
</Typography>
<Typography component="p">
{product.data.attributes.description}
</Typography>
您必须像这样直接访问其属性:
<Typography gutterBottom variant="h5" component="h2">
{product.attributes.name}
</Typography>
<Typography component="p">
{product.attributes.description}
</Typography>
编辑
这里是CodeSandbox project,代码简化了,并且没有调用Axios请求(因为it's restrticted),而是将数据存储在JSON文件中。您还应该将isLoading
初始化为true,或使Index
组件在包含某些数据之前不呈现:
class Index extends React.Component {
constructor(props){
super(props);
this.state = {
...
isLoading: true,
}
}
}
以下是更新了 的屏幕截图,该屏幕正常工作:
以及简化的<ProductsList/>
组件:
import React from "react";
const url = "https://stern-telecom-react-salman15.c9users.io";
class ProductsList extends React.Component {
constructor(props) {
super(props);
const { products, images } = this.props;
//const arrays = Object.values( {products} );
this.state = {
products,
images
//arrays
};
}
render() {
const { products, images } = this.state;
return (
<React.Fragment>
<p>Shop Products</p>
{console.log("PL", products, images)
// this.state.arrays.map(product =>
// <li key={product.objectID}>
// </li>
// )
}
<ul>
{products.map(product => (
<li key={product.key}>
<h4>{product.attributes.name}</h4>
<p>Description: {product.attributes.description}</p>
<p>Price: {product.attributes.display_price} </p>
<p>Images:</p>
<div>
{product.relationships.images.data.map(({ id }) => {
let image = images.find(image => image.id == id);
return image ? (
<img src={`${url}/${image.attributes.styles[1].url}`}/>
) : null;
})}
</div>
</li>
))}
</ul>
</React.Fragment>
);
}
}
export default ProductsList;
编辑2
要添加图像,这是一个非常简单的任务。您只需要将产品数组与图像结合起来并显示图像即可。检查更新的<ProductsList/>
组件。当然,您必须同时将products
和images
传递到<ProductsList/>
(const images = productsData.included;
)。检查更新后的CodeSandbox,<ProductsList/>
组件和屏幕截图。
编辑3
关于图像;每个图像都有一个styles
属性,该属性是一个大小不同的数组:
"included": [
{
"id": "5",
"type": "image",
"attributes": {
"viewable_type": "Spree::Variant",
"viewable_id": 4,
"styles": [
{
"url": "...",
"width": "48",
"height": "48"
},
{
"url": "...",
"width": "100",
"height": "100"
},
{
"url": "...",
"width": "240",
"height": "240"
},
{
"url": "...",
"width": "600",
"height": "600"
}
]
}
}
...
]
为了将图像映射到每个产品,我们必须使用product.relationships.images.data
映射每个产品中存储的所有图像,id
是具有type
和let image = images.find(image => image.id == id)
属性的对象数组。对于产品图像中的每个图像,我们使用48px
搜索图像数组,如果找到图像,则使用四个可用尺寸之一或所有可用尺寸(100px
,{ {1}},240px
,600px
);我选择image.attributes.styles[1].url
,因此显示了可用图像尺寸的第二个元素,即100px
尺寸图像:
product.relationships.images.data.map(({ id }) => {
let image = images.find(image => image.id == id);
return image ? (
<img src={`${url}/${image.attributes.styles[1].url}`}/>
) : null;
})
编辑4
如果每个产品需要获取一张图像,则可以使用一个函数来检查图像是否存在,然后从images数组中获取图像:
// Here we're using an inline function to get the product image
// You can also create a normal class function and use that instead
{product.relationships.images.data.length > 0 &&
(() => {
// { id } is the destructure of product.relationships.images.data[0]
// which means it extract the property id to a stand alone variable
const { id } = product.relationships.images.data[0];
const image = images.find(image => image.id == id);
return image ? (
<img src={`${url}/${image.attributes.styles[1].url}`} />
) : null;
})()
}
这是一个内联函数,可隔离其内容并立即执行:
(() => { ... })()
您可以了解有关Destructuring assignment
({ id } = object
)的更多信息。