Implementing "Load More" Functionality Inside a Cell with Accumulative Results

Hello,

I’m working on implementing a “Load More” functionality directly within a RedwoodJS Cell component. My goal is to allow users to fetch additional data upon clicking a “Load More” button, and then append these new results to the already displayed list of items, effectively accumulating the results without navigating away or reloading the entire dataset.

Here’s the challenge:
I am fairly new to Redwood and Graphql. I understand that Redwood Cells encapsulate the data fetching lifecycle and don’t directly expose functions like fetchMore as you might find in Apollo Client’s useQuery hook. Given this, I’m seeking guidance on the best way to achieve the following within a Cell:

  1. Trigger Additional Fetches: My thought was to just call a “fetchMore” like method from within the cell from a button and merge data together manually. But I don’t think “fetchMore” is exposed.
 const loadMore = async () => {
    const newOffset = offset + offsetIncrement
    const { data, error } = fetchMore({ // not available
      variables: {
        startDate,
        endDate,
        direction,
        limit: limit,
        offset: newOffset,
      },
    })
    if (error) {
      console.error('Error loading more', error)
    }
    // TODO: merge data 
    setNumberOfLoads(numberOfLoads + 1)
    setTotal(total + data.length)
    setOffset(newOffset)
  }
  1. Best Practices: Are there any recommended patterns or practices within the RedwoodJS framework for achieving this kind of functionality while maintaining the benefits of using Cells?

I have looked at a few posts like :

But I don’t understand the approaches mentioned there.

Any examples, suggestions, or insights on how to approach this would be greatly appreciated.

1 Like

@rob do we have a public example of fetchMore? I can think of a place you might use that but perhaps we need a public example.

If not, I can work on one.

We’ve got a pagination How-To but don’t see a Load More. I’ve built that into Spoke and could do a quick article about it…

This is what I came up with in Spoke. Here’s your typical Cell with a bunch of results:

export const QUERY = gql`
  query FindPosts($offset: Int) {
    posts(offset: $offset) {
      id
      title
      body
    }
  }
`

export const Success = ({ posts, queryResult: { fetchMore } }) => {
  return (
    <>
      <ul>
        {posts.map((post) => (
          <li key={post.id}>
            <Post
              post={post}
            />
          </li>
        ))}
      </ul>

      <LoadMoreButton
        collection={posts}
        fetchMore={fetchMore}
      />
    </>
  )
}

And then the <LoadMoreButton> can be used anywhere you want to show more results as long as you pass it a bunch of results and the fetchMore() function:

const LoadMoreButton = ({ collection, fetchMore }) => {
  const getMore = () => {
    fetchMore({ variables: { offset: collection.length } })
  }

  return (
    <button type="button" onClick={getMore}>
      Load More
    </button>
  )
}

I have a little more logic in the real code for determining if there are actually more to show (and hide the button if not), display a loading spinner while they’re incoming, etc.

1 Like

For others like me who might get confused as to why this does not work by itself, you need to update RedwoodApolloProvider (at least I had to)

I got this working for my chat in Qantbear and posted in Spoke on it as well with code:
https://spoke.run/RyanQuinn/qantbear/posts/1842

I used fetchMore then you can updateQuery and I just pull in the Query the Cell has/used.

fetchMore({
//skip the length of the chat
        variables: {
          initiativeSessionId,
          take,
          skip: data.initiativeChats.length,
        },
//passing in an abort controller
        context: {
          fetchOptions: {
            signal: controller.signal,
          },
        },
//here is the updateQuery
        updateQuery: (previousResult, { fetchMoreResult }) => {
          if (!fetchMoreResult) return previousResult
          return {
            initiativeChats: [
              ...previousResult.initiativeChats,
              ...fetchMoreResult.initiativeChats,
            ],
          }
        },
      })

Here is how it looks in app:

I have a ref to track scrolling and when it hits bottom it sends this, but this could also be attached to a load more button as @rob did as well.

1 Like

Awesome! I posted a reply on Spoke with how I did the “Load More” button (also using fetchMore()): https://spoke.run/RyanQuinn/qantbear/posts/1842#Post-1860