Skip to content

Commit 87a9b27

Browse files
committed
docs: Add server component example to REST intro
1 parent e24532f commit 87a9b27

File tree

7 files changed

+92
-33
lines changed

7 files changed

+92
-33
lines changed

docs/core/api/useSuspense.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ render(<ProfileList />);
239239

240240
Reactive [pagination](/rest/guides/pagination) is achieved with [mutable schemas](/rest/api/Collection)
241241

242-
<PaginationDemo />
242+
<PaginationDemo defaultTab="PostList" />
243243

244244
### Sequential
245245

docs/core/getting-started/data-dependency.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -157,21 +157,26 @@ function Navigation() {
157157
if (route.startsWith('detail'))
158158
return <PostDetail setRoute={setRoute} id={route.split('/')[1]} />;
159159

160-
return <><PostList setRoute={setRoute} /><LoadMore /></>;
160+
return (
161+
<>
162+
<PostList setRoute={setRoute} />
163+
<LoadMore />
164+
</>
165+
);
161166
}
162167

163168
function LoadMore() {
164169
const ctrl = useController();
165170
const posts = useQuery(PostResource.getList.schema);
166-
const [nextPage, isPending] = useLoading(
167-
() => ctrl.fetch(PostResource.getList.getPage, { page: 2 }),
171+
const [nextPage, isPending] = useLoading(() =>
172+
ctrl.fetch(PostResource.getList.getPage, { page: 2 }),
168173
);
169174
if (!posts || posts.length % 3 !== 0) return null;
170175
return (
171176
<center>
172-
<button onClick={nextPage}>{isPending ? "..." : 'Load more'}</button>
177+
<button onClick={nextPage}>{isPending ? '...' : 'Load more'}</button>
173178
</center>
174-
)
179+
);
175180
}
176181
render(<Navigation />);
177182
```
@@ -252,7 +257,7 @@ import { ProfileResource } from './ProfileResource';
252257
function ProfileList(): JSX.Element {
253258
const { data, loading, error } = useDLE(ProfileResource.getList);
254259
if (error) return <div>Error {`${error.status}`}</div>;
255-
if (loading || !data) return <Loading/>;
260+
if (loading || !data) return <Loading />;
256261
return (
257262
<div>
258263
{data.map(profile => (

docs/core/guides/ssr.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ To keep your data fresh and performant, you can use client components and [useSu
5757
```tsx title="app/todos/[userId]/page.tsx"
5858
'use client';
5959
import { useSuspense } from '@data-client/react';
60-
import { TodoResource } from '../../resources/Todo';
60+
import { TodoResource } from '@/resources/Todo';
6161

6262
export default function InteractivePage({ params }: { params: { userId: number } }) {
6363
const todos = useSuspense(TodoResource.getList, params);
@@ -74,7 +74,7 @@ However, if your data never changes, you can slightly decrease the javascript bu
7474
using a server component. Simply `await` the endpoint:
7575

7676
```tsx title="app/todos/[userId]/page.tsx"
77-
import { TodoResource } from '../../resources/Todo';
77+
import { TodoResource } from '@/resources/Todo';
7878

7979
export default async function StaticPage({ params }: { params: { userId: number } }) {
8080
const todos = await TodoResource.getList(params);

docs/core/shared/_pagination.mdx

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { postPaginatedFixtures } from '@site/src/fixtures/posts';
22
import HooksPlayground from '@site/src/components/HooksPlayground';
33

4-
<HooksPlayground defaultOpen="n" row fixtures={postPaginatedFixtures}>
4+
<HooksPlayground defaultOpen="n" row fixtures={postPaginatedFixtures} defaultTab={props.defaultTab}>
55

66
```ts title="User" collapsed
77
import { Entity } from '@data-client/rest';
@@ -73,30 +73,44 @@ interface Props {
7373
}
7474
```
7575

76-
```tsx title="PostList" {9}
76+
```tsx title="LoadMore" {7}
77+
import { useController, useLoading } from '@data-client/react';
78+
import { PostResource } from './Post';
79+
80+
export default function LoadMore({ cursor }: { cursor: string }) {
81+
const ctrl = useController();
82+
const [loadPage, isPending] = useLoading(
83+
() => ctrl.fetch(PostResource.getList.getPage, { cursor }),
84+
[cursor],
85+
);
86+
return (
87+
<center>
88+
<button onClick={loadPage} disabled={isPending}>
89+
{isPending ? '...' : 'Load more'}
90+
</button>
91+
</center>
92+
);
93+
}
94+
```
95+
96+
```tsx title="PostList" {7} collapsed
7797
import { useSuspense } from '@data-client/react';
7898
import PostItem from './PostItem';
99+
import LoadMore from './LoadMore';
79100
import { PostResource } from './Post';
80101

81102
export default function PostList() {
82103
const { posts, cursor } = useSuspense(PostResource.getList);
83-
const ctrl = useController();
84-
const handlePageLoad = () =>
85-
ctrl.fetch(PostResource.getList.getPage, { cursor });
86104
return (
87105
<div>
88106
{posts.map(post => (
89107
<PostItem key={post.pk()} post={post} />
90108
))}
91-
{cursor ? (
92-
<center>
93-
<button onClick={handlePageLoad}>Load more</button>
94-
</center>
95-
) : null}
109+
{cursor ? <LoadMore cursor={cursor} /> : null}
96110
</div>
97111
);
98112
}
99113
render(<PostList />);
100114
```
101115

102-
</HooksPlayground>
116+
</HooksPlayground>

docs/rest/README.md

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import TypeScriptEditor from '@site/src/components/TypeScriptEditor';
1414

1515
<PkgTabs pkgs="@data-client/rest" />
1616

17-
## Define the API
17+
## Define the Resources
1818

1919
[Resources](./api/resource.md) are a collection of `methods` for a given `data model`. [Entities](./api/Entity.md) and [Schemas](./api/schema.md) are the declarative _data model_.
2020
[RestEndpoint](./api/RestEndpoint.md) are the [_methods_](<https://en.wikipedia.org/wiki/Method_(computer_programming)>) on
@@ -142,19 +142,20 @@ TypeScript enforces the arguments specified with a prefixed colon like `:slug` i
142142
ArticleResource.get({ slug: 'use-reactive-data-client' });
143143
```
144144

145-
## Bind the data with Suspense
145+
## Render the data
146146

147147
<Tabs
148148
defaultValue="Single"
149149
values={[
150150
{ label: 'Single', value: 'Single' },
151151
{ label: 'List', value: 'List' },
152+
{ label: 'Server Component', value: 'server' },
152153
]}>
153154
<TabItem value="Single">
154155

155156
```tsx
156157
import { useSuspense } from '@data-client/react';
157-
import { ArticleResource } from 'api/article';
158+
import { ArticleResource } from '@/resources/Article';
158159

159160
export default function ArticleDetail({ slug }: { slug: string }) {
160161
const article = useSuspense(ArticleResource.get, { slug });
@@ -167,16 +168,22 @@ export default function ArticleDetail({ slug }: { slug: string }) {
167168
}
168169
```
169170

171+
:::info
172+
173+
[useSuspense()](/docs/api/useSuspense) acts like [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await), ensuring the data is available before returning. [Learn how to be declare your data dependencies](/docs/getting-started/data-dependency)
174+
175+
:::
176+
170177
</TabItem>
171178
<TabItem value="List">
172179

173180
```tsx
174181
import { useSuspense } from '@data-client/react';
175-
import { ArticleResource } from 'api/article';
182+
import { ArticleResource } from '@/resources/Article';
176183
import ArticleSummary from './ArticleSummary';
177184

178-
export default function ArticleList() {
179-
const articles = useSuspense(ArticleResource.getList);
185+
export default function ArticleList({ userId }: { userId?: number }) {
186+
const articles = useSuspense(ArticleResource.getList, { userId });
180187
return (
181188
<section>
182189
{articles.map(article => (
@@ -187,11 +194,41 @@ export default function ArticleList() {
187194
}
188195
```
189196

190-
</TabItem>
191-
</Tabs>
197+
:::info
192198

193199
[useSuspense()](/docs/api/useSuspense) acts like [await](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await), ensuring the data is available before returning. [Learn how to be declare your data dependencies](/docs/getting-started/data-dependency)
194200

201+
:::
202+
203+
</TabItem>
204+
<TabItem value="server">
205+
206+
```tsx title="app/articles/[userId]/page.tsx"
207+
import { useSuspense } from '@data-client/react';
208+
import { ArticleResource } from '@/resources/Article';
209+
import ArticleSummary from './ArticleSummary';
210+
211+
export default async function ArticleList({ params }: { params: { userId: number } }) {
212+
const articles = await ArticleResource.getList(params);
213+
return (
214+
<section>
215+
{articles.map(article => (
216+
<ArticleSummary key={article.pk()} article={article} />
217+
))}
218+
</section>
219+
);
220+
}
221+
```
222+
223+
:::warning
224+
225+
[Server Components](/docs/guides/ssr#server-components) makes the data static and un-mutable.
226+
227+
:::
228+
229+
</TabItem>
230+
</Tabs>
231+
195232
## Mutate the data
196233

197234
<Tabs
@@ -205,7 +242,7 @@ values={[
205242

206243
```tsx title="NewArticleForm.tsx"
207244
import { useController } from '@data-client/react';
208-
import { ArticleResource } from 'api/article';
245+
import { ArticleResource } from '@/resources/Article';
209246

210247
export default function NewArticleForm() {
211248
const ctrl = useController();
@@ -231,7 +268,7 @@ resolves to the new Resource created by the API. It will automatically be added
231268

232269
```tsx title="UpdateArticleForm.tsx"
233270
import { useController } from '@data-client/react';
234-
import { ArticleResource } from 'api/article';
271+
import { ArticleResource } from '@/resources/Article';
235272

236273
export default function UpdateArticleForm({ slug }: { slug: string }) {
237274
const article = useSuspense(ArticleResource.get, { slug });
@@ -260,7 +297,7 @@ resolves to the new Resource created by the API. It will automatically be added
260297

261298
```tsx title="ArticleWithDelete.tsx"
262299
import { useController } from '@data-client/react';
263-
import { Article, ArticleResource } from 'api/article';
300+
import { Article, ArticleResource } from '@/resources/Article';
264301

265302
export default function ArticleWithDelete({
266303
article,

examples/github-app/src/pages/NextPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { IssueResource } from '@/resources/Issue';
55

66
export default function NextPage({ q, page }: Props) {
77
const ctrl = useController();
8-
const [loadMore, loading] = useLoading(() =>
8+
const [loadMore, isPending] = useLoading(() =>
99
ctrl.fetch(IssueResource.search.getPage, {
1010
page,
1111
q,
1212
}),
1313
);
1414
return (
1515
<div style={{ textAlign: 'center', marginTop: 12 }}>
16-
{loading ? 'loading...' : <Button onClick={loadMore}>Load more</Button>}
16+
{isPending ? 'loading...' : <Button onClick={loadMore}>Load more</Button>}
1717
</div>
1818
);
1919
}

website/src/fixtures/posts.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ export const postPaginatedFixtures = [
234234
cursor: cursor + 1,
235235
};
236236
},
237+
delay: (...args) => {
238+
return args?.[0]?.cursor ? 200 : 0;
239+
},
237240
},
238241
];
239242

0 commit comments

Comments
 (0)