JSX mapping arrays stored inside a dynamic object (Next.js)

Viewed 497

I'm new to React/Next and having some difficulty displaying data returned by an API.

Here is the data's structure:

myBooks = {
  reading: [
    {
      title: 'book1',
      cover: 'cover1.jpg'
    }
  ],
  toRead: [
    {
      title: 'book2',
      cover: 'cover2.jpg'
    }
  ],
  read: [
    {
      title: 'book3',
      cover: 'cover3.jpg'
    },
    {
      title: 'book4',
      cover: 'cover4.jpg'
    }
  ]
}

This is how I'd like the data displayed:

Reading:
book1

To Read:
book2

Read:
book3
book4

I'd like this to be dynamic, so if I add another category like "favorites," those books show in their own section with a relevant header.

And here is my attempt to render this data, using nested .map functions:

{Object.keys(myBooks).map(category => {
    <h3>{category}</h3>
    category.map(book => (
        <p>{book.title}</p>
    ))
})}

My rationale:

  1. .map can't be used on an object, so I start by selecting the object's keys, which is what I call category (reading, toRead, read)
  2. Once I display the category, traverse down and list each book associated with that category

The error I am receiving:
TypeError: category.map is not a function

Suggesting that category is not an array, or does not exist. Any help would be greatly appreciated!

3 Answers

You might want to use Object.entries instead, this will give you access to each element's key and value.

{Object.entries(myBooks).map(([category, books]) => (
    <>
        <h3>{category}</h3>
        {books.map((book) => (
            <p>{book.title}</p>
        ))}
    </>
))}

The issue is that the category variable will be each key, which is a string. You should change the category.map to myBooks[category].map()

{Object.keys(myBooks).map(category => {
    <h3>{category}</h3>
    {myBooks[category].map(book => (
        <p>{book.title}</p>
    ))}
})}

Also, in this case, you don't want to use .map(). .map() is used when you want to modify the elements in the array - in this case, you just want to loop through them. Use .forEach() instead

{Object.keys(myBooks).forEach(category => {
    <h3>{category}</h3>
    {myBooks[category].forEach(book => (
        <p>{book.title}</p>
    ))}
})}
  1. the function inside the first map, must return just one JSX element.
  2. category is the key, so to get the value use : myBooks[category]
  3. category.map((book) => <p>{book.title}</p>); is a js statement, it must be between {}, and remove the semi-colon at the end.
{Object.keys(myBooks).map((category) => (
        <>
          <h3>{category}</h3>
          {myBooks[category].map((book) => (
            <p>{book.title}</p>
          ))}
        </>
      ))}
Related