0

I'm trying to make a ecommerce site using the commerce.js library. I have already 8 products in the library API. I have a Products.js component where I'm passing those array of product objects I got from App.js and again passing single product object to the Product.js component. To do this, I'm using mapping function to pass each object as props to the Product.js component. And I'm receiving this error. I tried commenting out the mapping block and only console logged the products variable from App.js and the prop products in Products.js, there I'm getting all the 8 products so no problem in there. But I don't get it why I'm getting this error in the mapping function.

App.js

import React, { useState, useEffect } from "react";
import { commerce } from "./lib/commerce";
import { Products, Navbar } from "./components";

function App() {
  const [products, setProducts] = useState([]);

  const fetchProduct = async () => {
    const data = await commerce.products.list();
    setProducts(data);
  };
  useEffect(() => {
    fetchProduct();
  }, []);

  console.log("products", products);

  return (
    <div className="App">
      <Navbar />
      <Products products={products} />
    </div>
  );
}

export default App;

Products.js

import React from "react";
import { Grid } from "@material-ui/core";
import Product from "./Product/Product";
import useStyles from "./style";

const Products = ({ products }) => {
  const classes = useStyles();

  return (
    <main className={classes.content}>
      <div className={classes.toolbar} />
      <Grid container justifyContent="center" spacing={4}>
        {products.data.map((product) => (
          <Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
            <Product product={product} />
          </Grid>
        ))}
      </Grid>
    </main>
  );
};

export default Products;

Product.js

import React from "react";
import {
  Card,
  CardMedia,
  CardContent,
  CardActions,
  Typography,
  IconButton,
} from "@material-ui/core";
import { AddShoppingCart } from "@material-ui/icons";
import useStyles from "./styles";

const Product = ({ product }) => {
  const classes = useStyles();
  return (
    <Card className={classes.root}>
      <CardMedia
        className={classes.media}
        image={product.media.source}
        title={product.name}
      />
      <CardContent>
        <div className={classes.cardContent}>
          <Typography variant="h5" gutterBottom>
            {product.name}
          </Typography>
          <Typography variant="h5">
            {product.price.formatted_with_symbol}
          </Typography>
        </div>
        <Typography
          variant="body2"
          dangerouslySetInnerHTML={{ __html: product.description }}
        />
      </CardContent>
      <CardActions disableSpacing className={classes.cardActions}>
        <IconButton aria-label="Add to Cart">
          <AddShoppingCart />
        </IconButton>
      </CardActions>
    </Card>
  );
};

export default Product;
4
  • 1
    const [products, setProducts] = useState([]); products starts off as an array, and arrays don't have a .data property on them. Did you mean to do a different initial value? Or perhaps did you mean to do products.map instead of products.data.map? Commented Sep 25, 2021 at 18:24
  • No when I got the data from the API it had like this products { data: [Array of my products], metadata: {...} So to map I had to go through the chaining process Commented Sep 25, 2021 at 18:26
  • 1
    Ok, then change your initial state so it has the same shape. It needs to be an object with a data property that is an array. ie, useState({ data: [] }) Commented Sep 25, 2021 at 18:27
  • could you please explain why I had to set like this, It is working this way but when I'm setting the API response in array eventually through chaining I'm accessing the data. But why I had to explicitly set the initial value to {data:[]} Commented Sep 25, 2021 at 18:32

2 Answers 2

4

In your App.js change your fetchProduct function as follows:

const fetchProduct = async = {
    const products = await commerce.products.list();
    setProducts(products.data);
}

and then in your Products.js map over the products instead of products.data,

      {products.map((product) => (
          <Grid item key={product.id} xs={12} sm={6} md={4} lg={3}>
            <Product product={product} />
          </Grid>
      ))}

EXPLANATION:
The initial State of products is an empty array, which is passed down to the products component, where you tried products.data.map() but products is an array (initially) and there is no data property on arrays, so it threw error.
There are several ways you can get around this, but if you are only using the products data from the response then you can go with the above approach, where you map over the products not products.data and setProducts() with the products array instead of products object.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your solution. I also applied the same method but here I used destructuring so const {data} = await commerce.products.list()
2

Do you can to attached screen with error?

I think, you try to read data before is are loading, you can prevent this by add something like this (products.data || []).map for example.. or change default state in App.js on useState({data:[]})

4 Comments

Thanks, I think that was the problem I was calling the mapping function before getting the api response. But In App.js I used async await and then saved the response in my variable. So a bit confused here.
useEffect with empty array in arguments, will work like componentDidMount , he will called after first render, when you else don't get the data
so can I just pass the data as argument in the useEffect so when I have the data it'll automatically re-render?
If you add data to the dependency array for usefetch, it will cause unnecessary rerenders and calls to fetchProduct function

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.