Infinite Scroll using Cells

Hi all!

I’m currently working on an app that requires infinite scroll. (So fetching the next “page” when reaching the end of the screen).

I tried doing this using fetchMore inside a Cell, to see how it would work.

According to my observations 2 options are possible:

  1. You set notifyOnNetworkStatusChange to true and the loading screen retriggers, hiding the already loaded content and showing your Loading component.
  2. You leave notifyOnNetworkStatusChange on false and there’s no loading screen, your content just appears at some point.

I would like to display a loader on the bottom of my screen while new content loads. To facilitate this I’ve created the following intermediary component:

import StoriesCell from 'src/components/StoriesCell'

import { useState, useCallback, useEffect } from 'react'

export const StoriesInfinite = ({ limit, where }) => {
  const [cursors, setCursors] = useState({})

  const addCursor = useCallback(newCursor => {
    setCursors(current => ({ ...current, [newCursor]: true }))
  }, [])

  useEffect(() => {
    setCursors({})
  }, [where])

  return (
    <>
      <StoriesCell first={limit} where={where} onReachingEnd={addCursor} />
      {Object.keys(cursors).map(cursorId => (
        <StoriesCell
          key={cursorId}
          first={limit}
          after={{ id: cursorId }}
          where={where}
          onReachingEnd={addCursor}
        />
      ))}
    </>
  )
}

What it does is add a cursor to a list of cursors (made unique by using an Object, to prevent duplication). Each cursor/page then renders it’s own Cell.

It works pretty well for me!

Since infinite scrolling is a quite common functionality nowadays I decided to open a topic to ask what you think. Is there anything I missed int he fetchMore method? I wasn’t familiar with it before! Is there an easier way to do this?

3 Likes

Hi @Robert I’ve not tried to do something like this as it’s definitely the first use-case I’ve seen, but at first glance, this is a great example/use-case! Appreciate you posting here for others to see and for discussion.

@peterp quick reactions about improving this or potential downsides?

This is like more than a year later - sorry, but I was wondering how you got fetchmore working with Apollo? I’m not sure what the redwood client does OOTB with merging

So the idea, as I understand it, behind fetchMore is that your cell would call it internally when it was ready for more data and then would be re-rendered with the merged set of old and new data when the fetch is complete. This avoids the awkwardness of either having to externally update the cell or split the full dataset between cells at the a higher level.

The problem with this in Redwood is that Apollo makes this work by merging the results of the subsequent queries together in its cache. Redwood has this cache working under the hood, but doesn’t expose the configuration parameter to us, so we can’t tell it which fields it should merge.

Edit: This is now fixed by exposing the cache configuration to the end user.

@dom Do you have any insight into

you have to implement your own ApolloProvider because the Redwood one doesn’t expose any way to customize the caching strategies.

I know you’ve looked into how to manage Apollo client caching.

@zackdotcomputer Also, did you see the new Apollo Client release?

The Core Team is discussing possible upgrades here too – and looks like the are InMemeoryCache improvements.

BTW - I have not tried to customize the GraphQL client or the provider, but I did see:

Note that you’re free to use any of Apollo’s other hooks, you’ll just have to import them from @apollo/client instead. In particular, these two hooks might come in handy:

useApolloClient Access your instance of ApolloClient

Could that access the cache to change to meet you needs?

I did - I am now using it locally in my modified Apollo provider. Though it has some nice improvements to the in memory cache, I am not sure it fixes the issue of needing to provide a type policy to the cache to get behaviors like pagination merging?

useApolloClient is closer - using the cache variable in the client I could access and manually modify the cache after a query completes, letting me merge the new values in. However, that’s pretty painfully manual compared to specifying a merge policy and having the client do it for me. Sadly, the client doesn’t appear to expose a way to modify the type policy after the cache has been instantiated. The best point for doing that still seems to be at the moment the cache is created in the Provider.

Having looked at the code now for RedwoodApolloProvider, I’m pretty sure it is OK to expose a pathway for customizing that type policy. Redwood isn’t passing one in on its own, so there isn’t any risk of conflicting with one generated by the framework.

1 Like

I personally had a use case for this too - would love to understand the best practices on this and what others have figured out.

@Robert are you still using the same solution?

@zackdotcomputer it sounds like your latest PR helps with this use case quite a bit?

@viperfx Yup I think my latest PR resolves this for people. It’s merged now, so hopefully will be in 0.35.3 / 0.36.0 (whatever the next release gets numbered as).

1 Like

0.36 is the plan!

Does this change how other requests behave or does it just add the ability to allow infinite requests?

This change does not alter the default behavior at all. The change is only to expose the cache configuration through the redwood apollo provider. If you want to use Apollo’s features that rely on the cache, like pagination and fetchMore, then you need to use that to specify your own cache type policy, so Apollo knows which types to merge and how.