···87878888Here we keep an array of all `variables` we've encountered and use them to render their
8989respective `result` page. This only rerenders the additional page rather than having a long
9090-list that constantly changes. [You can find a full code example of this pattern in our example folder on the topic of Graphcache pagination.](https://github.com/urql-graphql/urql/tree/main/examples/with-graphcache-pagination)
9090+list that constantly changes. [You can find a full code example of this pattern in our example folder on the topic of pagination.](https://github.com/urql-graphql/urql/tree/main/examples/with-pagination)
91919292-We also do not need to use our normalized cache to achieve this. As long as we're able to split individual lists up into chunks across components, we can also solve this problem entirely in UI code. [Read our example code on how to achieve this.](https://github.com/urql-graphql/urql/tree/main/examples/with-pagination)
9292+This code doesn't take changing variables into account, which will affect the cursors. For an
9393+example that takes full infinite scrolling into account, [you can find a full code example of an
9494+extended pattern in our example folder on the topic of infinite pagination.](https://github.com/urql-graphql/urql/tree/main/examples/with-infinite--pagination)
93959496## Prefetching data
9597
+8
docs/graphcache/local-resolvers.md
···452452with separate components per page in environments like React Native, where a `FlatList` would
453453require a flat, infinite list of items.
454454455455+> **Note:** If you don't need a flat array of results, you can also achieve infinite pagination
456456+> with only UI code. [You can find a code example of UI infinite pagination in our example folder.](https://github.com/urql-graphql/urql/tree/main/examples/with-pagination)
457457+458458+[You can find a code example of infinite pagination with Graphcahce in our example folder.](https://github.com/urql-graphql/urql/tree/main/examples/with-graphcache-pagination).
459459+Please keep in mind that this patterns has some limitations when you're handling cache updates.
460460+Deleting old pages from the cache selectively may be difficult, so the UI pattern in the above
461461+note is preferred.
462462+455463### Simple Pagination
456464457465Given we have a schema that uses some form of `offset` and `limit` based pagination, we can use the
+3-1
examples/README.md
···55| [`with-svelte`](./with-svelte) | Shows a basic query in `@urql/svelte` with Svelte. |
66| [`with-vue3`](./with-vue3) | Shows a basic query in `@urql/vue` with Vue 3. |
77| [`with-next`](./with-next) | Shows some examples with `next-urql` in Next.js with the default, `getStaticProps` and `getServerSideProps`. |
88-| [`with-pagination`](./with-pagination) | Shows how to generically set up infinite pagination with `urql` in UI code. |
88+| [`with-pagination`](./with-pagination) | Shows how to generically set up pagination with `urql` in UI code. |
99+| [`with-infinite-pagination`](./with-infinite-pagination) | Shows how to generically set up infinite scrolling pagination with `urql` in UI code. |
910| [`with-apq`](./with-apq) | Shows Automatic Persisted Queries with `@urql/exchange-persisted-fetch`. |
1011| [`with-graphcache-updates`](./with-graphcache-updates) | Shows manual cache updates with `@urql/exchange-graphcache`. |
1112| [`with-graphcache-pagination`](./with-graphcache-pagination) | Shows the automatic infinite pagination helpers from `@urql/exchange-graphcache`. |
···1314| [`with-refresh-auth`](./with-refresh-auth) | Shows an example of authentication with refresh tokens using `@urql/exchange-auth`. |
1415| [`with-retry`](./with-retry) | Shows how to set up `@urql/exchange-retry` for retrying failed operations. |
1516| [`with-defer-stream-directives`](./with-defer-stream-directives) | Demonstrates `urql` and `@urql/exchange-graphcache` with built-in support for `@defer` and `@stream`. |
1717+| [`with-subscriptions-via-fetch`](./with-subscriptions-via-fetch) | Demonstrates `urql` executing subscriptions with a GraphQL Yoga API via the `fetchExchange`. |
+41
examples/with-infinite-pagination/README.md
···11+# With Infinite Pagination (in React)
22+33+This example shows how to implement **infinite scroll** pagination with `urql`
44+in your React UI code.
55+66+It's slightly different than the [`with-pagination`](../with-pagination) example
77+and shows how to implement a full infinitely scrolling list with only your UI code,
88+while fulfilling the following requirements:
99+1010+- Unlike with [`with-graphcache-pagination`](../with-graphcache-pagination),
1111+ the `urql` cache doesn't have to know about your infinite list, and this works
1212+ with any cache, even the document cache
1313+- Unlike with [`with-pagination`](../with-pagination), your list can use cursors,
1414+ and each page can update, while keeping the variables for the next page dynamic.
1515+- It uses no added state, no extra processing of lists, and you need no effects.
1616+1717+In other words, unless you need a flat array of items
1818+(e.g. unless you’re using React Native’s `FlatList`), this is the simplest way
1919+to implement an infinitely scrolling, paginated list.
2020+2121+This example is also reapplicable to other libraries, like Svelte or Vue.
2222+2323+To run this example install dependencies and run the `start` script:
2424+2525+```sh
2626+yarn install
2727+yarn run start
2828+# or
2929+npm install
3030+npm run start
3131+```
3232+3333+This example contains:
3434+3535+- The `urql` bindings and a React app with a client set up in [`src/App.js`](src/App.jsx)
3636+ - This also contains a search input which is used as input for the GraphQL queries
3737+- All pagination components are in [`src/SearchResults.jsx`](src/SearchResults.jsx)
3838+ - The `SearchRoot` component loads the first page of results and renders `SearchPage`
3939+ - The `SearchPage` displays cached results, and otherwise only starts a network request on
4040+ a button press
4141+ - The `Package` component is used for each result item
···11+import React, { useCallback } from 'react';
22+import { gql, useQuery } from 'urql';
33+44+// We define a fragment, just to define the data
55+// that our item component will use in the results list
66+const packageFragment = gql`
77+ fragment SearchPackage on Package {
88+ id
99+ name
1010+ latest: version(selector: "latest") {
1111+ version
1212+ }
1313+ }
1414+`;
1515+1616+// The main query fetches the first page of results and gets our `PageInfo`
1717+// This tells us whether more pages are present which we can query.
1818+const rootQuery = gql`
1919+ query SearchRoot($searchTerm: String!, $resultsPerPage: Int!) {
2020+ search(query: $searchTerm, first: $resultsPerPage) {
2121+ edges {
2222+ cursor
2323+ node {
2424+ ...SearchPackage
2525+ }
2626+ }
2727+ pageInfo {
2828+ hasNextPage
2929+ endCursor
3030+ }
3131+ }
3232+ }
3333+3434+ ${packageFragment}
3535+`;
3636+3737+// We split the next pages we load into a separate query. In this example code,
3838+// both queries could be the same, but we keep them separate for educational
3939+// purposes.
4040+// In a real app, your "root query" would often fetch more data than the search page query.
4141+const pageQuery = gql`
4242+ query SearchPage(
4343+ $searchTerm: String!
4444+ $resultsPerPage: Int!
4545+ $afterCursor: String!
4646+ ) {
4747+ search(query: $searchTerm, first: $resultsPerPage, after: $afterCursor) {
4848+ edges {
4949+ cursor
5050+ node {
5151+ ...SearchPackage
5252+ }
5353+ }
5454+ pageInfo {
5555+ hasNextPage
5656+ endCursor
5757+ }
5858+ }
5959+ }
6060+6161+ ${packageFragment}
6262+`;
6363+6464+// This is the <SearchRoot> component that we render in `./App.jsx`.
6565+// It accepts our variables as props.
6666+const SearchRoot = ({ searchTerm = 'urql', resultsPerPage = 10 }) => {
6767+ const [rootResult] = useQuery({
6868+ query: rootQuery,
6969+ variables: {
7070+ searchTerm,
7171+ resultsPerPage,
7272+ },
7373+ });
7474+7575+ if (rootResult.fetching) {
7676+ return <em>Loading...</em>;
7777+ }
7878+7979+ // Here, we render the results as a list into a fragment, and if `hasNextPage`
8080+ // is truthy, we immediately render <SearchPage> for the next page.
8181+ const connection = rootResult.data?.search;
8282+ return (
8383+ <>
8484+ {connection?.edges?.length === 0 ? <strong>No Results</strong> : null}
8585+8686+ {connection?.edges.map(edge => (
8787+ <Package key={edge.cursor} node={edge.node} />
8888+ ))}
8989+9090+ {/* The <SearchPage> component receives the same props, plus the `afterCursor` for its variables */}
9191+ {connection?.pageInfo.hasNextPage ? (
9292+ <SearchPage
9393+ searchTerm={searchTerm}
9494+ resultsPerPage={resultsPerPage}
9595+ afterCursor={connection.pageInfo.endCursor}
9696+ />
9797+ ) : rootResult.fetching ? (
9898+ <em>Loading...</em>
9999+ ) : null}
100100+ </>
101101+ );
102102+};
103103+104104+// The <SearchPage> is rendered for each page of results, except for the root query.
105105+// It renders *itself* recursively, for the next page of results.
106106+const SearchPage = ({ searchTerm, resultsPerPage, afterCursor }) => {
107107+ // Each <SearchPage> fetches its own page results!
108108+ const [pageResult, executeQuery] = useQuery({
109109+ query: pageQuery,
110110+ // Initially, we *only* want to display results if, they're cached
111111+ requestPolicy: 'cache-only',
112112+ // We don't want to run the query if we don't have a cursor (in this example, this will never happen)
113113+ pause: !afterCursor,
114114+ variables: {
115115+ searchTerm,
116116+ resultsPerPage,
117117+ afterCursor,
118118+ },
119119+ });
120120+121121+ // We only load more results, by allowing the query to make a network request, if
122122+ // a button has pressed.
123123+ // In your app, you may want to do this automatically if the user can see the end of
124124+ // your list, e.g. via an IntersectionObserver.
125125+ const onLoadMore = useCallback(() => {
126126+ // This tells the query above to execute and instead of `cache-only`, which forbids
127127+ // network requests, we now allow them.
128128+ executeQuery({ requestPolicy: 'cache-first' });
129129+ }, [executeQuery]);
130130+131131+ if (pageResult.fetching) {
132132+ return <em>Loading...</em>;
133133+ }
134134+135135+ const connection = pageResult.data?.search;
136136+ return (
137137+ <>
138138+ {/* If our query has nodes, we render them here. The page renders its own results */}
139139+ {connection?.edges.map(edge => (
140140+ <Package key={edge.cursor} node={edge.node} />
141141+ ))}
142142+143143+ {/* If we have a next page, we now render it recursively! */}
144144+ {/* As before, the next <SearchPage> will not fetch immediately, but only query from cache */}
145145+ {connection?.pageInfo.hasNextPage ? (
146146+ <SearchPage
147147+ searchTerm={searchTerm}
148148+ resultsPerPage={resultsPerPage}
149149+ afterCursor={connection.pageInfo.endCursor}
150150+ />
151151+ ) : pageResult.fetching ? (
152152+ <em>Loading...</em>
153153+ ) : null}
154154+155155+ {!connection && !pageResult.fetching ? (
156156+ <button type="button" onClick={onLoadMore}>
157157+ Load more
158158+ </button>
159159+ ) : null}
160160+ </>
161161+ );
162162+};
163163+164164+// This is the component that then renders each result item
165165+const Package = ({ node }) => (
166166+ <section>
167167+ <strong>{node.name}</strong>
168168+ <em>@{node.latest.version}</em>
169169+ </section>
170170+);
171171+172172+export default SearchRoot;
+6
examples/with-infinite-pagination/src/index.jsx
···11+import React from 'react';
22+import { createRoot } from 'react-dom/client';
33+44+import App from './App';
55+66+createRoot(document.getElementById('root')).render(<App />);
+7
examples/with-infinite-pagination/vite.config.js
···11+import { defineConfig } from 'vite';
22+import react from '@vitejs/plugin-react';
33+44+// https://vitejs.dev/config/
55+export default defineConfig({
66+ plugins: [react()],
77+});
+5-3
examples/with-pagination/README.md
···11# With Pagination (in React)
2233-This example shows how to implement infinite pagination with `urql` in your React UI code. It
44-renders several pages as fragments with one component managing the variables for the page queries.
55-This example is also reapplicable to other libraries, like Svelte or Vue.
33+This example shows how to implement pagination with `urql` in your React UI code.
44+55+It renders several pages as fragments with one component managing the variables
66+for the page queries. This example is also reapplicable to other libraries,
77+like Svelte or Vue.
6879To run this example install dependencies and run the `start` script:
810