apollographql / apollographql/apollo-client-integrations

Pagination & Caching with Server-Preloaded Client Components

Open
#552 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
556
Forks
53
PR merge metrics
No merged PRs in 30d

Description

In Next.js I'm using a Server Component to preload data into a Client Component using `preloadQuery` and `useSuspenseQuery`. If javascript is enabled, the Client Component uses `fetchMore` + `offsetLimitPagination()` to do an infinite scroll. This all works fine.

When javascript is disabled, a classic link-based pagination is displayed which uses a `page` url query to display each page of results. The `page` query string value is passed to the component and used to set the `offset` variable before the query is executed.

Server component:
```
export default function RecipeEntriesList({ pageNum }: { pageNum: number }) {
// query variables are initialized in the
// Server Component then passed to the Client Component
const queryLimit = 10;

const queryVariables: QueryVariables = {
section: ["recipes"],
limit: queryLimit,
offset: (pageNum - 1) * queryLimit, // 0 if on the first page
};

return (



);
}
```

Client component:

```
export default function RecipeEntriesListClient({
queryVariables,
}: {
queryVariables: QueryVariables;
}) {
// offset is mutable in the client component
// so we copy the initial value into a ref
const queryOffset = useRef(queryVariables.offset);

const { data, error, networkStatus, fetchMore } =
useSuspenseQuery(GET_RECIPE_ENTRIES, {
variables: queryVariables, // defined in the Recipe Entries server component
});

const inViewRef = useOnInView((inView) => {
if (inView && data && data.recipeList.length < data.entryCount) {
fetchMore({
variables: {
offset: (queryOffset.current = data.recipeList.length),
},
});
}
});

// ...
```

This does not work as expected. The entries from page 1 are displayed no matter which page query string is being accessed. I assume this has to do with how Apollo is caching the entries, but I don't know what I'm doing wrong. I have a separate `InMemoryCache` for the server component that lists `offset` as a keyArg but it doesn't seem to have an effect:

```
const keyArgs = ["section", "offset"];

const queryCache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
entries: {
keyArgs: keyArgs
},
entryCount: {
keyArgs: keyArgs,
},
},
},
},
});
```
I'm surprised this is happening since the server cache gets thrown out immediately anyway so I don't really understand what's wrong. Here's a [CodeSandbox with the full setup](https://codesandbox.io/p/devbox/apollo-client-rsc-pagination-demo-ymqqwl).

Contributor guide

No contributing guide indexed for this repository

Research direction

Reproduce the linked CodeSandbox and start with RecipeEntriesList, RecipeEntriesListClient, and the separate InMemoryCache typePolicies. Trace how the page-derived offset reaches preloadQuery and useSuspenseQuery, then verify that each page shows its requested entries while client-side fetchMore and infinite scroll still work.

Written by the indexing model from the issue text.

Assessment

Tech stack
next.js, typescript
Domain
frontend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.