簡體   English   中英

如何使用多個對象映射API [Spree API V2和ReactJS]

[英]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,
    }
  }
}

這是一個更新的截圖,它工作:

CodeSandbox截圖

簡化的<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/>組件。 當然,您必須將productsimages傳遞給<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映射存儲在每個產品中的所有圖像,這是一個具有idtype屬性的對象數組。 對於產品圖像中的每個圖像,我們使用let image = images.find(image => image.id == id)搜索圖像數組,如果我們找到圖像,那么我們使用四種可用尺寸中的一種或者全部可用尺寸( 48px100px240px600px ); 我選擇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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM