1

I am trying to extract the length of an array while mapping it.

Here is what happens:

First I have an array of objects. Each object has a key of posts where I store the posts for that object. My code takes all the posts from all the objects and maps them to a new array so that I can show all the posts from all the objects to the user on the front end.

I'd like to show only 10 posts at a time. So I put a .slice(0, page * 10) - the variable page is controlled by a button at the bottom of the page. If the user hits the button, the page then increases the number of posts on the screen.

This all works great. BUT - I'd like to be able to count the total number of posts and only show the button when there are more posts available. Is there a way to extract the number of posts while still allowing it to map the results from this function below?

{
  bandTypes === "all"
    ? allBands
      .filter(band => {
        if (showType !== 'Show Type') {
          return band.showTypes.includes(showType)
        } else {
          return band
        }
      })
      .reduce(
        (allPosts, band) =>
          allPosts.concat(
            (band.youtube.length > 0 &&
              band.bandBio !== "n/a" &&
              band.bandGenre !== "n/a")
              ? band.posts.map((post) => ({ post, band }))
              : []
          ),
        []
      )
      .sort((a, b) => new Date(b.post.date) - new Date(a.post.date))
      .slice(0, page * 10)
      .map(({ post, band }) => <div key={uuidv4()}>{convertPost(post, band)}</div>)
    : null
}

It would be great if I could just put an anonymous function in there somewhere that sets the state to the length of the array.

Syntle
  • 5,168
  • 3
  • 13
  • 34
Nick McLean
  • 601
  • 1
  • 9
  • 23

1 Answers1

0

I think that trying to accomplish this without any temporary variables is not going to be very efficient and probably would be quite ugly.

I think you should first create the array of all posts and then simply use it's length inside the return of your component.

Here's an example of how I would do it:

const MyComponent = () => {
    let allPosts = []
    if (bandTypes === "all") {
        allPosts = allBands
            .filter((band) => {
                if (showType !== "Show Type") {
                    return band.showTypes.includes(showType)
                } else {
                    return band
                }
            })
            .reduce(
                (allPosts, band) =>
                    allPosts.concat(
                        band.youtube.length > 0 &&
                            band.bandBio !== "n/a" &&
                            band.bandGenre !== "n/a"
                            ? band.posts.map((post) => ({
                                  post,
                                  band,
                              }))
                            : []
                    ),
                []
            )
            .sort((a, b) => new Date(b.post.date) - new Date(a.post.date))
    }

    return (
        <div>
            {allPosts.slice(0, page * 10).map(({ post, band }) => (
                <div key={uuidv4()}>{convertPost(post, band)}</div>
            ))}
            {allPosts.length < page * 10 && <button>Show more posts</button>}
        </div>
    )
}

(BTW using something like uuidv4 for the key isn't ideal, because React is less efficient with rendering then. It's better base the key on something that is unique to each post and doesn't change for each render, for example an id from the database or something of this sort)

Adam Jeliński
  • 1,708
  • 1
  • 10
  • 21
  • Thanks again Adam! I've been away and not able to try it yet, but it makes a lot of sense. - In the mean time... I think that you are right at about UUID loading slow - Does this load slow outside of react too? https://stackoverflow.com/questions/62314291/foreach-happens-too-fast-with-async-await?noredirect=1#comment110209002_62314291 – Nick McLean Jun 10 '20 at 23:15
  • The UUID implementation is probably really quick and not really a problem. Just it's best for `keys` to give all components a stable identity, so React knows which ones changed, moved or were removed with fewer computations. Take a look [here](https://reactjs.org/docs/lists-and-keys.html#keys) for the most reliable information. – Adam Jeliński Jun 10 '20 at 23:35
  • I see! Thanks man! Man I have learned so much by doing all this! Really appropriate it all! – Nick McLean Jun 10 '20 at 23:38
  • Hey Adam! I just got a chance to reformat the code this morning to work with this - Yes! It works great. I also change the react keys to have specific ids found form the posts in the database! I am doing a ton of testing now - Maybe a few last min features to add to the site... But other than that! Wow! I am so impressed with my first site! Thanks for your help, really! – Nick McLean Jun 21 '20 at 14:31
  • Hey Adam! Do you know any reason that git or npm run build would exclude images/routes for my application on heroku? - https://stackoverflow.com/questions/62723427/public-folder-in-heroku-not-working-with-images-and-routes – Nick McLean Jul 03 '20 at 22:38
  • Hey Adam! Long time no see! I am having some trouble rendering multiple instances of the same component in some react! Would you have any time to check this out? https://stackoverflow.com/questions/63414947/react-usestate-setstate-error-not-a-function-when-multiple-instances-of-compnen – Nick McLean Aug 14 '20 at 14:43