Search code examples
javascriptreactjsfetch

Loading json data with Fetch to pass it to React Component as prop


So I'm struggling with this task for a while and I really can not know what to do. The problem is when i try to fetch the data from "/menu/items" and then pass it as prop to Card.js I get the information that "Cannot read properties of undefined (reading 'name')" in Card.js.

So I have to Components:

Menu.js

const Menu = (props) => {
  const [orders, onAddOrder] = useState([]);
  const [menuItems, setMenuItems] = useState([]);

  useEffect(() => {
    getItems();
  });

  const getItems = async () => {
    const response = await fetch("/menu/items");
    const data = await response.json();
    setMenuItems(data);
  };

  return (
    <div className="menu">
      <div className="menu-main__container">
        <article>
          <Card order={menuItems[0]} />
          <Card order={menuItems[1]} />
          <Card order={menuItems[2]} />
        </article>
        <article>
          <Card order={menuItems[3]} />
          <Card order={menuItems[4]} />
          <Card order={menuItems[5]} />
        </article>
        <article>
          <Card order={menuItems[6]} />
          <Card order={menuItems[7]} />
          <Card order={menuItems[8]} />
        </article>
      </div>
    </div>
  );
};

which is parent component of Card Component

Card.js

const Card = (props) => {
  const [order, onAdd] = useState(props.order);

  const onAddOrderHandler = () => {
    console.log(order);
  };

  return (
    <div className="container__order">
      <div className="order__details">
        <img src={KebapImg} className="details__img" />
        <h3>{props.order.name}</h3>
        <p>{props.order.desc}</p>
        <div className="details__price-buy">
          <h1>{props.order.price}zł</h1>
          <input
            type="submit"
            value="Take it!"
            className="price-buy__btn"
          />
        </div>
      </div>
    </div>
  );
};

/menu/items as json file:

{
      id: 1,
      name: "iKebap Pro Max Super",
      price: 22,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 2,
      name: "iKebap Pro Max",
      price: 21,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 3,
      name: "iKebap Classic",
      price: 15,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 4,
      name: "iKebap California Reaper",
      price: 18,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 5,
      name: "iKebap Overpriced",
      price: 37,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 6,
      name: "iKebap Small",
      price: 10,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 7,
      name: "iKebap Wege",
      price: 17,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 8,
      name: "iKebap American",
      price: 16,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    },
    {
      id: 9,
      name: "iKebap Góralski",
      price: 17,
      desc: "Lorem Ipsum dolor sit amet, consectetur adipsicing elit",
    }

All I want to do is to pass the fetched item to prop and render it as a new component.


Solution

  • First and foremost, add an empty dependency array in the useEffect, cause you don't wanna run it on every re-render.

    useEffect(() => {
        getItems();
    }, []);
    

    Second, you are indeed trying to access an undefined property. On first render you are still resolving the fetch. So your menuItems array is still empty, and you're passing undefined values menuItems[0].

    All you need to do is waiting for the next render.

    const Menu = (props) => {
      const [orders, onAddOrder] = useState([]);
      const [menuItems, setMenuItems] = useState([]);
    
      useEffect(() => {
        getItems();
      }, []);
    
      const getItems = async () => {
        const response = await fetch("/menu/items");
        const data = await response.json();
        setMenuItems(data);
      };
    
      // ADD THIS LINE
      if (!menuItems.length) return <div>Loading...</div>
    
      return (
        <div className="menu">
          <div className="menu-main__container">
            <article>
              <Card order={menuItems[0]} />
              <Card order={menuItems[1]} />
              <Card order={menuItems[2]} />
            </article>
            <article>
              <Card order={menuItems[3]} />
              <Card order={menuItems[4]} />
              <Card order={menuItems[5]} />
            </article>
            <article>
              <Card order={menuItems[6]} />
              <Card order={menuItems[7]} />
              <Card order={menuItems[8]} />
            </article>
          </div>
        </div>
      );
    };