[英]How to map API with multiple objects [Spree API V2 & ReactJS]
我正在建立一個網店,使用ReactJS作為前端,使用Spree(Ruby)作為后端。
Spree提供了一個API解決方案,可以將前端和后端與一個和另一個連接起來。
我正在嘗試顯示帶有產品圖像的產品,但Spree的API是以產品圖像和產品不在同一對象中的特定方式設置的。
API響應是:
{
(holds products)data: [],
(Holds product images)included:[],
}
我的目標是創建一個ul
,顯示產品信息和產品圖像。
我試圖映射我的API鏈接
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
ProductList頁面
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
我期望得到的結果是產品信息和圖像
獲取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
構造函數中的products數組:
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
...
此外,product對象不包含任何名為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項目 ,代碼簡化,無需調用Axios請求(因為它已被重新設置 ),而是將數據放在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
, 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
)的更多信息。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.