0
import { useState, useEffect } from "react";

function GoodsList(props) {
    const [products, setProducts] = useState(null);
    const goods = props.goods;
    let productCards = [];

    fetch('products.json').then(async response => {
        setProducts(await response.json());
    })

    let content;
    useEffect(() => {
        if (products != null) {
            for (let x = 0; x < products[goods].length; x++) {
                productCards.push(
                    <div className="productCard" key={x}>
                        <div className="productPicture">
                            <img src={products[goods[x].pic]} /></div>
                        <div className="productName">{products[goods[x].name]}</div>
                    </div>
                )
            }
            content = (
                <div className="list">
                    {productCards}
                </div>
            );
            console.log(productCards);
        }
    }
    )
    return content;
}

Initially I put the return of the JSX inside the useEffect function, but it was causing an error. Now it just doesn't return anything without any errors in the console, but the console.log inside useeffect outputs indefinitely somehow.

1
  • Please clarify your specific problem or provide additional details to highlight exactly what you need. As it's currently written, it's hard to tell exactly what you're asking. Commented Apr 22, 2023 at 14:12

2 Answers 2

2

There's a lot wrong here, to be honest. To name a few...

  1. You're fetching the data on every render, regardless of whether you need to or not.
  2. You're updating state on every render, which triggers a re-render on every render, indefinitely.
  3. There's no dependency array in the useEffect call, so that also triggers on every render.
  4. The component only ever returns undefined (the default initial value of content) so no actual rendered output will ever be seen.

Overall, the key changes to make are:

  1. All of your rendering logic is in useEffect, but should be in the main body of the component to produce a rendered result.
  2. The operation to fetch the data is in the main body of the component, but should be in useEffect so it only happens when it needs to.

So first let's wrap this in a useEffect:

useEffect(() => {
  fetch('products.json').then(async response => {
    setProducts(await response.json());
  });
}, [setProducts]);

So now this operation to fetch the data will only happen once, when the component first loads. (And technically would happen again any time setProducts ever changes, but setProducts won't change since it also comes from a React-managed hook.)

Then after that, you seem to be drastically over-thinking how to build your rendered output. Simplify. Just return the JSX markup you want based on the state of products. For example:

return (
  <div className="list">
    {products && products[goods].map((good, i) => (
      <div className="productCard" key={i}>
        <div className="productPicture">
          <img src={good.pic]} /></div>
        <div className="productName">{good.name]}</div>
      </div>
    )}
  </div>
);
Sign up to request clarification or add additional context in comments.

2 Comments

I would probably initialise products to an empty array rather than null too.
Thank you very much! I'm just learning React and you helped a lot
2
        /* Add some basic styling to make
// Get products from API
fetch('/products')
  .then(response => response.json())
  .then(products => {
    // Render product listings
    const productListings = products.map(product => {
      return `
        <div>
          <h2>${product.name}</h2>
          <p>${product.price}</p>
          <button>Add to Cart</button>
        </div>
      `;
    });

    document.getElementById('product-list

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.