DragonBorn
DragonBorn

Reputation: 1809

How to return incomplete jsx from map function

I have data in an array of objects as below:

history = [
  {item: cake, calories: 120, datetime: 2022-11-16 07:51:26},
  {item: chicken, calories: 250, datetime: 2022-11-16 13:48:46},
  {item: pizza, calories: 420, datetime: 2022-11-25 11:13:42}
];

I want to render a div with a heading for the date and group all items with same dates in a list. I am using my map function like below:

function renderHistory () {
  let date;

  return props.history.map((item, i) => {
    const dateAdded = item.datetime.split(" ")[0];

    if (date !== dateAdded) {
      date = dateAdded;
      return (
        <>
          <div>
            <h2>{dateAdded}</h2>
            <li>{item.item} - {item.calories} calories</li>
        </>
      )
    }

    return (
      <>
          <li>{item.item} - {item.calories} calories</li>
        </div>
      </>
    )

  })
}

I get an error Expected corresponding JSX closing tag for <div>

If I return like below the second item gets out of the div.

return props.history.map((item, i) => {

  const dateAdded = item.datetime.split(" ")[0];

  if (date !== dateAdded) {
    date = dateAdded;
    return (
      <div>
        <h2>{dateAdded}</h2>
        <li>{item.item} - {item.calories} calories</li>
      </div>
    )
  }

  return (
    <li>{item.item} - {item.calories} calories</li>
  )

})

How do I put all items with same date in a single div?

Upvotes: 0

Views: 71

Answers (2)

Daniel Smith
Daniel Smith

Reputation: 189

import "./styles.css";
const foodItems = [
  { item: "cake", calories: 120, datetime: "2022-11-16 07:51:26" },
  { item: "chicken", calories: 250, datetime: "2022-11-16 13:48:46" },
  { item: "pizza", calories: 420, datetime: "2022-11-25 11:13:42" }
];

const foodItemsGroupedByDate = {};

foodItems.map((foodItem) => {
  const date = foodItem.datetime.split(" ")[0];
  foodItemsGroupedByDate[date] = foodItemsGroupedByDate[date] || [];
  foodItemsGroupedByDate[date].push({
    item: foodItem.item,
    calories: foodItem.calories
  });
});

const foodItemsArray = Object.entries(foodItemsGroupedByDate);

function RenderFoodItems({ items, date }) {
  return (
    <>
      <div>{date}</div>
      {items.map((item) => {
        return <div>{item.item} - {item.calories}</div>;
      })}
    </>
  );
}
export default function App() {
  return (
    <div className="App">
      {foodItemsArray.map((item) => {
        return <RenderFoodItems date={item[0]} items={item[1]} />;
      })}
    </div>
  );
}

Try something like this, tested in codesandbox.

Upvotes: 1

Wraithy
Wraithy

Reputation: 2056

React under hood transforms JSX <div><h2>text</h2</div> into

React.createElement('div', undefined, React.createElement('h2',undefined,'text'))

what are you trying to do is impossible with this architecture.

Instead you need to find some other approach. Maybe group data by date and then render. Something like

function renderHistory () {
const grouped = props.history.reduce((acc,item)=>{
  const dateAdded = item.datetime.split(" ")[0];
   return {...acc, [dateAdded]: [...acc[dateAdded], item]}
  },{});

  return Object.keys(grouped).map((dayAdded) => {
     return (
     <div key={dayAdded}>
        <h2>{dateAdded}</h2>
       { grouped[dayAdded].map((item)=>(
         <li key={item.item+dayAdded}>{item.item} - {item.calories} calories</li>)
         )}
      </div>
    )

  })
}

Upvotes: 1

Related Questions