Skip to content

Commit d0ff00b

Browse files
committed
docs: Add pagination example to useSuspense() page
1 parent fd9842f commit d0ff00b

File tree

4 files changed

+123
-108
lines changed

4 files changed

+123
-108
lines changed

docs/core/api/useSuspense.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Tabs from '@theme/Tabs';
1212
import TabItem from '@theme/TabItem';
1313
import GenericsTabs from '@site/src/components/GenericsTabs';
1414
import ConditionalDependencies from '../shared/\_conditional_dependencies.mdx';
15+
import PaginationDemo from '../shared/\_pagination.mdx';
1516
import HooksPlayground from '@site/src/components/HooksPlayground';
1617
import { RestEndpoint } from '@data-client/rest';
1718
import TypeScriptEditor from '@site/src/components/TypeScriptEditor';
@@ -375,6 +376,12 @@ export default function ArticleList({ page }: { page: string }) {
375376

376377
</TypeScriptEditor>
377378

379+
### Pagination
380+
381+
Reactive [pagination](/rest/guides/pagination) is achieved with [mutable schemas](/rest/api/Collection)
382+
383+
<PaginationDemo />
384+
378385
### Server Side Rendering
379386

380387
[Server Side Rendering](../guides/ssr.md) to incrementally stream HTML,

docs/core/shared/_pagination.mdx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import { postPaginatedFixtures } from '@site/src/fixtures/posts';
2+
import HooksPlayground from '@site/src/components/HooksPlayground';
3+
4+
<HooksPlayground defaultOpen="n" row fixtures={postPaginatedFixtures}>
5+
6+
```ts title="User" collapsed
7+
import { Entity } from '@data-client/rest';
8+
9+
export class User extends Entity {
10+
id = 0;
11+
name = '';
12+
username = '';
13+
email = '';
14+
phone = '';
15+
website = '';
16+
17+
get profileImage() {
18+
return `https://i.pravatar.cc/64?img=${this.id + 4}`;
19+
}
20+
21+
pk() {
22+
return `${this.id}`;
23+
}
24+
static key = 'User';
25+
}
26+
```
27+
28+
```ts title="Post" {22,24} collapsed
29+
import { Entity, createResource } from '@data-client/rest';
30+
import { User } from './User';
31+
32+
export class Post extends Entity {
33+
id = 0;
34+
author = User.fromJS();
35+
title = '';
36+
body = '';
37+
38+
pk() {
39+
return this.id?.toString();
40+
}
41+
static key = 'Post';
42+
43+
static schema = {
44+
author: User,
45+
};
46+
}
47+
export const PostResource = createResource({
48+
path: '/posts/:id',
49+
schema: Post,
50+
paginationField: 'cursor',
51+
}).extend('getList', {
52+
schema: { posts: new schema.Collection([Post]), cursor: '' },
53+
});
54+
```
55+
56+
```tsx title="PostItem" collapsed
57+
import { type Post } from './Post';
58+
59+
export default function PostItem({ post }: Props) {
60+
return (
61+
<div className="listItem spaced">
62+
<Avatar src={post.author.profileImage} />
63+
<div>
64+
<h4>{post.title}</h4>
65+
<small>by {post.author.name}</small>
66+
</div>
67+
</div>
68+
);
69+
}
70+
71+
interface Props {
72+
post: Post;
73+
}
74+
```
75+
76+
```tsx title="PostList" {9}
77+
import { useSuspense } from '@data-client/react';
78+
import PostItem from './PostItem';
79+
import { PostResource } from './Post';
80+
81+
export default function PostList() {
82+
const { posts, cursor } = useSuspense(PostResource.getList);
83+
const ctrl = useController();
84+
const handlePageLoad = () =>
85+
ctrl.fetch(PostResource.getList.getPage, { cursor });
86+
return (
87+
<div>
88+
{posts.map(post => (
89+
<PostItem key={post.pk()} post={post} />
90+
))}
91+
{cursor ? (
92+
<center>
93+
<button onClick={handlePageLoad}>Load more</button>
94+
</center>
95+
) : null}
96+
</div>
97+
);
98+
}
99+
render(<PostList />);
100+
```
101+
102+
</HooksPlayground>

docs/rest/guides/pagination.md

Lines changed: 7 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@ title: Pagination of REST data with Reactive Data Client
33
sidebar_label: Pagination
44
---
55

6+
<head>
7+
<meta name="docsearch:pagerank" content="10"/>
8+
</head>
9+
610
import StackBlitz from '@site/src/components/StackBlitz';
7-
import { postPaginatedFixtures } from '@site/src/fixtures/posts';
811
import HooksPlayground from '@site/src/components/HooksPlayground';
12+
import PaginationDemo from '../../core/shared/\_pagination.mdx';
913

1014
# Rest Pagination
1115

@@ -14,105 +18,7 @@ import HooksPlayground from '@site/src/components/HooksPlayground';
1418
In case you want to append results to your existing list, rather than move to another page
1519
[Resource.getList.getPage](../api/createResource.md#getpage) can be used as long as [paginationField](../api/createResource.md#paginationfield) was provided.
1620

17-
<HooksPlayground defaultOpen="n" row fixtures={postPaginatedFixtures}>
18-
19-
```ts title="User" collapsed
20-
import { Entity } from '@data-client/rest';
21-
22-
export class User extends Entity {
23-
id = 0;
24-
name = '';
25-
username = '';
26-
email = '';
27-
phone = '';
28-
website = '';
29-
30-
get profileImage() {
31-
return `https://i.pravatar.cc/64?img=${this.id + 4}`;
32-
}
33-
34-
pk() {
35-
return `${this.id}`;
36-
}
37-
static key = 'User';
38-
}
39-
```
40-
41-
```ts title="Post" {22,24} collapsed
42-
import { Entity, createResource } from '@data-client/rest';
43-
import { User } from './User';
44-
45-
export class Post extends Entity {
46-
id = 0;
47-
author = User.fromJS();
48-
title = '';
49-
body = '';
50-
51-
pk() {
52-
return this.id?.toString();
53-
}
54-
static key = 'Post';
55-
56-
static schema = {
57-
author: User,
58-
};
59-
}
60-
export const PostResource = createResource({
61-
path: '/posts/:id',
62-
schema: Post,
63-
paginationField: 'cursor',
64-
}).extend('getList', {
65-
schema: { results: new schema.Collection([Post]), cursor: '' },
66-
});
67-
```
68-
69-
```tsx title="PostItem" collapsed
70-
import { type Post } from './Post';
71-
72-
export default function PostItem({ post }: Props) {
73-
return (
74-
<div className="listItem spaced">
75-
<Avatar src={post.author.profileImage} />
76-
<div>
77-
<h4>{post.title}</h4>
78-
<small>by {post.author.name}</small>
79-
</div>
80-
</div>
81-
);
82-
}
83-
84-
interface Props {
85-
post: Post;
86-
}
87-
```
88-
89-
```tsx title="PostList" {9}
90-
import { useSuspense } from '@data-client/react';
91-
import PostItem from './PostItem';
92-
import { PostResource } from './Post';
93-
94-
export default function PostList() {
95-
const { results, cursor } = useSuspense(PostResource.getList);
96-
const ctrl = useController();
97-
const handlePageLoad = () =>
98-
ctrl.fetch(PostResource.getList.getPage, { cursor });
99-
return (
100-
<div>
101-
{results.map(post => (
102-
<PostItem key={post.pk()} post={post} />
103-
))}
104-
{cursor ? (
105-
<center>
106-
<button onClick={handlePageLoad}>Load more</button>
107-
</center>
108-
) : null}
109-
</div>
110-
);
111-
}
112-
render(<PostList />);
113-
```
114-
115-
</HooksPlayground>
21+
<PaginationDemo />
11622

11723
Don't forget to define our [Resource's](../api/createResource.md) [paginationField](../api/createResource.md#paginationfield) and
11824
correct [schema](../api/createResource.md#schema)!
@@ -125,7 +31,7 @@ export const PostResource = createResource({
12531
paginationField: 'cursor',
12632
}).extend('getList', {
12733
// highlight-next-line
128-
schema: { results: new schema.Collection([Post]), cursor: '' },
34+
schema: { posts: new schema.Collection([Post]), cursor: '' },
12935
});
13036
```
13137

website/src/fixtures/posts.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,19 +183,19 @@ export const postPaginatedFixtures = [
183183
async response(...args) {
184184
const cursor = args?.[0]?.cursor ?? 1;
185185
const userId = args?.[0]?.userId;
186-
let results = Object.values(extendedEntities);
186+
let posts = Object.values(extendedEntities);
187187
if (userId) {
188-
results = Object.values(extendedEntities).filter(
188+
posts = Object.values(extendedEntities).filter(
189189
post => post.userId === args[0].userId,
190190
);
191191
}
192192
const PAGE_SIZE = 3;
193-
results = results.slice((cursor - 1) * PAGE_SIZE, PAGE_SIZE * cursor);
193+
posts = posts.slice((cursor - 1) * PAGE_SIZE, PAGE_SIZE * cursor);
194194
// we are modifying the post later so we need to shallow copy it
195-
results = results.map(post => ({ ...post }));
195+
posts = posts.map(post => ({ ...post }));
196196
// get users to merge
197197
await Promise.all(
198-
results.map(post =>
198+
posts.map(post =>
199199
post.userId ?
200200
UserResource.get({ id: post.userId })
201201
.then(user => {
@@ -208,9 +208,9 @@ export const postPaginatedFixtures = [
208208
),
209209
);
210210
if (PAGE_SIZE * cursor >= Object.keys(extendedEntities).length)
211-
return { results, cursor: null };
211+
return { posts, cursor: null };
212212
return {
213-
results,
213+
posts,
214214
cursor: cursor + 1,
215215
};
216216
},

0 commit comments

Comments
 (0)