Try to infinite scroll pagination

Hi, everyone, I’m trying to do an infinite scroll (at least a load more) function in my app using graphql and Apollo nevertheless the Apollo documentation is around Offset-based pagination method or Cursor-based pagination but Strapi allowed us to use only start and limit parameters,

How can I do a an infinite scroll or a load more function with start and limit parameters ?

Is there a way to have access to Offset, cursor or other pagination arguments ?

The offset is basically using the limit + start as you mentioned. Setting the limit to say 25 and your start at 0, then just increase the start by the limit

(I’m using REST as an example just to show an easy example, but the same applies GraphQL)

localhost:1337/somemodel?_limit=25&_start=0
localhost:1337/somemodel?_limit=25&_start=25
localhost:1337/somemodel?_limit=25&_start=50
localhost:1337/somemodel?_limit=25&_start=75
localhost:1337/somemodel?_limit=25&_start=100
localhost:1337/somemodel?_limit=25&_start=125
ect

With GraphQL you can send the limit and start as variables (I’m using a limit of 3 just to show a clear example, and this is one of my personal projects)

start 0, limit 3

start 3, limit 3


For the implementation in your frontend, that largely depends on your framework.

3 Likes

Hey DMehaffy, and thank again for your answer, actually I’m using graphql with Apollo and react native, im completely lost aha :sweat_smile:

Do you know if it is possible to use the fetchmore function from Apollo or it is better to use the mutation ?

We don’t directly support the fetchmore option (at least that I’m aware of) and mutations are only for adding new data or updating it. In your case you are only fetching the data so mutations wouldn’t really fit here (It’s the equivalent of a REST POST/PUT request, where as a query is a REST GET)

the thing that I don’t understand is that with your method it will refetch all data whereas I want to add data to the previous one (with start) so is there a particular method to add data or I need to refetch it anyway ?

In the link that you provide, he use fetchmore function :sweat:

maybe update query ?

You would cache the first response client side, fetch the next and append it your client side cache, and repeat. The default fetchmore (forgive me because I’ve never used it) is something that’s built into Apollo but I have never seen it mentioned. I could be entirely wrong, the way I’m seeing it is as a function server side and to my knowledge we have never implemented it.

1 Like

Yeah in rereading that doc I linked (sorry about that) they are referencing a __typename that needs to be implemented in the GQL resolver, we only have this in our dynamic zones and not the actual model resolvers themselves.

Without the __typename that fetchmore function won’t work as you can’t define a type.

1 Like

by cache do you mean localStorage ?

Any kind of state storage on the client, I’m not a frontend dev so I’m not super familiar with good storage mediums. (Devops is more my focus)

Let me gently poke @soupette, @HichamELBSI to see what they think.

1 Like

thank you so much

hey, do you any new from them :slight_smile:

At the moment they are both super busy, hopefully someone in the community that may have implemented this before can add some more information.

1 Like

Hey DMehaffy, I finally succeed (at least I think :sweat_smile:), see you for a next question aha

Oh nice, can you share your implementation as a few others have asked about it as well.

sure :

//Initial pagination states:

 `   const [Skip, setSkip] = useState(10);
        const [limit, setLimit] = React.useState(10);
      const [starte, setStarte] = React.useState(0);

So I use Usequery from Apollo which allows me to use the fetch more function :

    const {
      data: data_next_drops,
      loading: loading_next_drops,
      error: error_next_drops,
      fetchMore: fetchMoreNext,
     refetch: refetchNext,
  } = useQuery(GET_ALL_NEXT_DROPS, {
  variables: {
    date: date,
    limit: limit,
    start: starte,
    resellIndex: resellFilter,
    brandCategorieName: brandFilter
},
  });

then I just need to create that function like this :

 const loadMore = async () => {
    setLoading(true);
    fetchMoreNext({
      variables: {
        date: date,
        limit: 10,
        start: Skip,
      },
      updateQuery: (preveResult, { fetchMoreResult }) => {
        setSkip(Skip + limit);
        fetchMoreResult.drops = [
          ...preveResult.drops,
          ...fetchMoreResult.drops,
        ];
//It copy the previous result with the new one
        setLoading(false);
        return fetchMoreResult;
      },
    });
  };
1 Like

Sorry for the delay, I didn’t see your answer :wink:

1 Like

@Gabriel_Eliade I’ve experienced same issue here.
I found that method is really simple so I modified a bit and it works well now. Here’s my code.
I named it as startLimitPagination

import {
  FieldPolicy,
  Reference,
} from '@apollo/client';

type KeyArgs = FieldPolicy<any>['keyArgs'];
// A basic field policy that uses options.args.{offset,limit} to splice
// the incoming data into the existing array. If your arguments are called
// something different (like args.{start,count}), feel free to copy/paste
// this implementation and make the appropriate changes.
export function startLimitPagination<T = Reference>(
  keyArgs: KeyArgs = false,
): FieldPolicy<T[]> {
  return {
    keyArgs,
    merge(existing, incoming, {args}) {
      const merged = existing ? existing.slice(0) : [];
      const start = args ? args.start : merged.length;
      const end = start + incoming.length;
      for (let i = start; i < end; ++i) {
        merged[i] = incoming[i - start];
      }
      return merged;
    },
  };
}
1 Like

very nice, and it’s with the new Apollo pagination policy ! thank you very much !