@modern-js/main-doc 0.0.0-nightly-20231007160533 → 0.0.0-nightly-20231009160555
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/en/apis/app/hooks/src/routes.mdx +1 -1
- package/docs/en/guides/advanced-features/rspack-start.mdx +1 -1
- package/docs/en/guides/advanced-features/ssr.mdx +17 -17
- package/docs/en/guides/basic-features/data/_category_.json +4 -0
- package/docs/en/guides/basic-features/{data-fetch.mdx → data/data-fetch.mdx} +35 -27
- package/docs/en/guides/basic-features/data/data-write.mdx +241 -0
- package/docs/en/guides/basic-features/routes.mdx +7 -7
- package/docs/en/tutorials/first-app/c05-loader.mdx +2 -2
- package/docs/en/tutorials/first-app/c06-model.mdx +4 -4
- package/docs/en/tutorials/first-app/c07-container.mdx +3 -3
- package/docs/zh/apis/app/hooks/src/routes.mdx +1 -1
- package/docs/zh/guides/advanced-features/rspack-start.mdx +1 -1
- package/docs/zh/guides/advanced-features/ssr.mdx +16 -16
- package/docs/zh/guides/basic-features/data/_category_.json +4 -0
- package/docs/zh/guides/basic-features/{data-fetch.mdx → data/data-fetch.md} +31 -27
- package/docs/zh/guides/basic-features/data/data-write.mdx +236 -0
- package/docs/zh/guides/basic-features/routes.mdx +7 -7
- package/docs/zh/tutorials/first-app/c05-loader.mdx +2 -2
- package/docs/zh/tutorials/first-app/c06-model.mdx +3 -3
- package/docs/zh/tutorials/first-app/c07-container.mdx +3 -3
- package/package.json +5 -5
@@ -54,7 +54,7 @@ The `routes/[id]/page.tsx` file will be converted to the `/:id` route. Except fo
|
|
54
54
|
|
55
55
|
In the component, you can use [useParams](/apis/app/runtime/router/router#useparams) to obtain the corresponding named parameter.
|
56
56
|
|
57
|
-
When using the [loader](/guides/basic-features/data-fetch#the-loader-function) function to obtain data, `params` will be passed as an input parameter to the `loader` function, and the corresponding parameter can be obtained through the attribute of `params`.
|
57
|
+
When using the [loader](/guides/basic-features/data/data-fetch#the-loader-function) function to obtain data, `params` will be passed as an input parameter to the `loader` function, and the corresponding parameter can be obtained through the attribute of `params`.
|
58
58
|
|
59
59
|
## Layout Component
|
60
60
|
|
@@ -26,7 +26,7 @@ After the project is created, you can experience the project by running `pnpm ru
|
|
26
26
|
When using Rspack as the bundler, the following Features are temporarily unavailable as some of the capabilities are still under development and we will provide support in the future.
|
27
27
|
|
28
28
|
- Storybook Devtool
|
29
|
-
- The usage of [useLoader](/guides/basic-features/data-fetch.html) in Client Side Rendering
|
29
|
+
- The usage of [useLoader](/guides/basic-features/data/data-fetch.html) in Client Side Rendering
|
30
30
|
|
31
31
|
:::
|
32
32
|
|
@@ -22,8 +22,8 @@ export default defineConfig({
|
|
22
22
|
|
23
23
|
Modern.js provides a Data Loader that simplifies data fetching for developers working with SSR and CSR. Each routing module, such as `layout.tsx` and `page.tsx`, can define its own Data Loader:
|
24
24
|
|
25
|
-
```ts title="src/routes/page.
|
26
|
-
export
|
25
|
+
```ts title="src/routes/page.data.ts"
|
26
|
+
export const loader = () => {
|
27
27
|
return {
|
28
28
|
message: 'Hello World',
|
29
29
|
};
|
@@ -49,7 +49,7 @@ Developers should still be mindful of data fallback, including `null` values or
|
|
49
49
|
|
50
50
|
1. When requesting a page through client-side routing, Modern.js sends an HTTP request. The server receives the request and executes the corresponding Data Loader function for the page, then returns the execution result as a response to the browser.
|
51
51
|
|
52
|
-
2. When using Data Loader, data is fetched before rendering. Modern.js also supports obtaining data during component rendering. For more related content, please refer to [Data Fetch](/guides/basic-features/data-fetch).
|
52
|
+
2. When using Data Loader, data is fetched before rendering. Modern.js also supports obtaining data during component rendering. For more related content, please refer to [Data Fetch](/guides/basic-features/data/data-fetch).
|
53
53
|
|
54
54
|
:::
|
55
55
|
|
@@ -158,10 +158,10 @@ Using SPR in Modern.js is very simple. Just add the PreRender component to your
|
|
158
158
|
|
159
159
|
Here is a simulated component that uses the useLoaderData API. The request in the Data Loader takes 2 seconds to consume.
|
160
160
|
|
161
|
-
```tsx title="page.
|
161
|
+
```tsx title="page.data.ts"
|
162
162
|
import { useLoaderData } from '@modern-js/runtime/router';
|
163
163
|
|
164
|
-
export
|
164
|
+
export const loader = async () => {
|
165
165
|
await new Promise((resolve, reject) => {
|
166
166
|
setTimeout(() => {
|
167
167
|
resolve(null);
|
@@ -307,9 +307,9 @@ export const loader = () => {
|
|
307
307
|
|
308
308
|
The two methods mentioned above will both bring some mental burden to developers. In real business scenarios, we found that most of the mixed Node/Web code appears in data requests.
|
309
309
|
|
310
|
-
Therefore, Modern.js has designed a [Data Fetch](/guides/basic-features/data-fetch) to separate CSR and SSR code based on [Nested Routing](/guides/basic-features/routes).
|
310
|
+
Therefore, Modern.js has designed a [Data Fetch](/guides/basic-features/data/data-fetch) to separate CSR and SSR code based on [Nested Routing](/guides/basic-features/routes).
|
311
311
|
|
312
|
-
We can separate **data requests from component code** by using independent files. Write the component logic in `routes/page.tsx` and write the data request logic in `routes/page.
|
312
|
+
We can separate **data requests from component code** by using independent files. Write the component logic in `routes/page.tsx` and write the data request logic in `routes/page.data.ts`.
|
313
313
|
|
314
314
|
```ts title="routes/page.tsx"
|
315
315
|
export default Page = () => {
|
@@ -317,9 +317,9 @@ export default Page = () => {
|
|
317
317
|
}
|
318
318
|
```
|
319
319
|
|
320
|
-
```ts title="routes/page.
|
320
|
+
```ts title="routes/page.data.tsx"
|
321
321
|
import fse from 'fs-extra';
|
322
|
-
export
|
322
|
+
export const loader = () => {
|
323
323
|
const file = fse.readFileSync('./myfile');
|
324
324
|
return {
|
325
325
|
...
|
@@ -329,7 +329,7 @@ export default () => {
|
|
329
329
|
|
330
330
|
## Remote Request
|
331
331
|
|
332
|
-
When initiating interface requests in SSR, developers sometimes encapsulate isomorphic request tools themselves. For some interfaces that require passing user cookies, developers can use the ['useRuntimeContext'](/guides/basic-features/data-fetch#route-loader) API to get the request header for implementation.
|
332
|
+
When initiating interface requests in SSR, developers sometimes encapsulate isomorphic request tools themselves. For some interfaces that require passing user cookies, developers can use the ['useRuntimeContext'](/guides/basic-features/data/data-fetch#route-loader) API to get the request header for implementation.
|
333
333
|
|
334
334
|
It should be noted that the obtained request header is for HTML requests, which may not be suitable for API requests. Therefore, ** don't passed through all request headers **.
|
335
335
|
|
@@ -361,7 +361,7 @@ The streaming SSR of Modern.js is implemented based on React Router, and the mai
|
|
361
361
|
|
362
362
|
### Return async data
|
363
363
|
|
364
|
-
```ts title="page.
|
364
|
+
```ts title="page.data.ts"
|
365
365
|
import { defer, type LoaderFunctionArgs } from '@modern-js/runtime/router';
|
366
366
|
|
367
367
|
interface User {
|
@@ -373,7 +373,7 @@ export interface Data {
|
|
373
373
|
data: User;
|
374
374
|
}
|
375
375
|
|
376
|
-
export
|
376
|
+
export const loader = ({ params }: LoaderFunctionArgs) => {
|
377
377
|
const userId = params.id;
|
378
378
|
|
379
379
|
const user = new Promise<User>(resolve => {
|
@@ -393,7 +393,7 @@ export default ({ params }: LoaderFunctionArgs) => {
|
|
393
393
|
|
394
394
|
`defer` can receive both asynchronous and synchronous data at the same time. For example:
|
395
395
|
|
396
|
-
```ts title="page.
|
396
|
+
```ts title="page.data.ts"
|
397
397
|
// skip some codes
|
398
398
|
|
399
399
|
export default ({ params }: LoaderFunctionArgs) => {
|
@@ -430,7 +430,7 @@ With the `<Await>` component, you can retrieve the data asynchronously returned
|
|
430
430
|
```tsx title="page.tsx"
|
431
431
|
import { Await, useLoaderData } from '@modern-js/runtime/router';
|
432
432
|
import { Suspense } from 'react';
|
433
|
-
import type { Data } from './page.
|
433
|
+
import type { Data } from './page.data';
|
434
434
|
|
435
435
|
const Page = () => {
|
436
436
|
const data = useLoaderData() as Data;
|
@@ -461,7 +461,7 @@ export default Page;
|
|
461
461
|
:::warning Warning
|
462
462
|
When importing types from a Data Loader file, it is necessary to use the `import type` syntax to ensure that only type information is imported. This can avoid packaging Data Loader code into the bundle file of the front-end product.
|
463
463
|
|
464
|
-
Therefore, the import method here is: `import type { Data } from './page.
|
464
|
+
Therefore, the import method here is: `import type { Data } from './page.data'`;
|
465
465
|
:::
|
466
466
|
|
467
467
|
You can also retrieve asynchronous data returned by the Data Loader using `useAsyncValue`. For example:
|
@@ -504,10 +504,10 @@ export default Page;
|
|
504
504
|
The `errorElement` property of the `<Await>` component can be used to handle errors thrown when the Data Loader executes or when a child component renders.
|
505
505
|
For example, we intentionally throw an error in the Data Loader function:
|
506
506
|
|
507
|
-
```ts title="page.
|
507
|
+
```ts title="page.data.ts"
|
508
508
|
import { defer } from '@modern-js/runtime/router';
|
509
509
|
|
510
|
-
export
|
510
|
+
export const loader = () => {
|
511
511
|
const data = new Promise((resolve, reject) => {
|
512
512
|
setTimeout(() => {
|
513
513
|
reject(new Error('error occurs'));
|
@@ -14,13 +14,19 @@ It should be noted that these APIs do not help applications initiate requests, b
|
|
14
14
|
Modern.js recommends using [conventional routing](/guides/basic-features/routes) for routing management. Through Modern.js's [conventional (nested) routing](/guides/basic-features/routes#conventional-routing), each routing component (`layout.ts` or `page.ts`) can have a same-named `loader` file. The `loader` file needs to export a function that will be executed before the component is rendered to provide data for the routing component.
|
15
15
|
|
16
16
|
:::info
|
17
|
-
Modern.js v1 supports fetching data via [useLoader](</guides/basic-features/data-fetch.html#useloader-(old-version)>), which is no longer the recommended usage. We do not recommend mixing the two except during the migration process.
|
17
|
+
Modern.js v1 supports fetching data via [useLoader](</guides/basic-features/data/data-fetch.html#useloader-(old-version)>), which is no longer the recommended usage. We do not recommend mixing the two except during the migration process.
|
18
|
+
|
19
|
+
:::
|
20
|
+
|
21
|
+
:::warning
|
22
|
+
- In the previous version, Modern.js Data Loader was defined in the `loader` file. In later versions, we recommend defining it in the `data` file, while we will maintain compatibility with the `loader` file.
|
23
|
+
- In the `data` file, the corresponding `loader` needs to be exported with a name。
|
18
24
|
|
19
25
|
:::
|
20
26
|
|
21
27
|
### Basic Example
|
22
28
|
|
23
|
-
Routing components such as `layout.ts` or `page.ts` can define a same-named `loader` file. The function exported by the `
|
29
|
+
Routing components such as `layout.ts` or `page.ts` can define a same-named `loader` file. The function exported by the `data` file provides the data required by the component, and then the data is obtained in the routing component through the `useLoaderData` function, as shown in the following example:
|
24
30
|
|
25
31
|
```bash
|
26
32
|
.
|
@@ -28,16 +34,16 @@ Routing components such as `layout.ts` or `page.ts` can define a same-named `loa
|
|
28
34
|
├── layout.tsx
|
29
35
|
└── user
|
30
36
|
├── layout.tsx
|
31
|
-
├── layout.
|
37
|
+
├── layout.data.ts
|
32
38
|
├── page.tsx
|
33
|
-
└── page.
|
39
|
+
└── page.data.ts
|
34
40
|
```
|
35
41
|
|
36
42
|
Define the following code in the file:
|
37
43
|
|
38
44
|
```ts title="routes/user/page.tsx"
|
39
45
|
import { useLoaderData } from '@modern-js/runtime/router';
|
40
|
-
import type { ProfileData } from './page.
|
46
|
+
import type { ProfileData } from './page.data.ts';
|
41
47
|
|
42
48
|
export default function UserPage() {
|
43
49
|
const profileData = useLoaderData() as ProfileData;
|
@@ -45,19 +51,19 @@ export default function UserPage() {
|
|
45
51
|
}
|
46
52
|
```
|
47
53
|
|
48
|
-
```ts title="routes/user/page.
|
54
|
+
```ts title="routes/user/page.data.ts"
|
49
55
|
export type ProfileData = {
|
50
56
|
/* some types */
|
51
57
|
};
|
52
58
|
|
53
|
-
export
|
59
|
+
export const loader = async (): Promise<ProfileData> => {
|
54
60
|
const res = await fetch('https://api/user/profile');
|
55
61
|
return await res.json();
|
56
62
|
};
|
57
63
|
```
|
58
64
|
|
59
65
|
:::caution
|
60
|
-
Here, routing components and `
|
66
|
+
Here, routing components and `data` files share a type, so the `import type` syntax should be used.
|
61
67
|
|
62
68
|
:::
|
63
69
|
|
@@ -81,7 +87,7 @@ The `loader` function has two input parameters:
|
|
81
87
|
When the route file is accessed through `[]`, it is used as [dynamic routing](/guides/basic-features/routes#dynamic-routing), and the dynamic routing fragment is passed as a parameter to the `loader` function:
|
82
88
|
|
83
89
|
```tsx
|
84
|
-
// routes/user/[id]/page.
|
90
|
+
// routes/user/[id]/page.data.tsx
|
85
91
|
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
86
92
|
|
87
93
|
export default async ({ params }: LoaderFunctionArgs) => {
|
@@ -100,10 +106,10 @@ When accessing `/user/123`, the parameter of the `loader` function is `{ params:
|
|
100
106
|
A common usage scenario is to get query parameters through `request`:
|
101
107
|
|
102
108
|
```tsx
|
103
|
-
// routes/user/[id]/page.
|
109
|
+
// routes/user/[id]/page.data.ts
|
104
110
|
import { LoaderFunctionArgs } from '@modern-js/runtime/router';
|
105
111
|
|
106
|
-
export
|
112
|
+
export const loader = async ({ request }: LoaderFunctionArgs) => {
|
107
113
|
const url = new URL(request.url);
|
108
114
|
const userId = url.searchParams.get('id');
|
109
115
|
return queryUser(userId);
|
@@ -152,8 +158,8 @@ function loader() {
|
|
152
158
|
In the `loader` function, errors can be handled by throwing an `error` or a `response`. When an error is thrown in the `loader` function, Modern.js will stop executing the code in the current `loader` and switch the front-end UI to the defined [`ErrorBoundary`](/guides/basic-features/routes#error-handling) component:
|
153
159
|
|
154
160
|
```tsx
|
155
|
-
// routes/user/profile/page.
|
156
|
-
export
|
161
|
+
// routes/user/profile/page.data.tsx
|
162
|
+
export const loader = async function loader() {
|
157
163
|
const res = await fetch('https://api/user/profile');
|
158
164
|
if (!res.ok) {
|
159
165
|
throw res;
|
@@ -185,7 +191,7 @@ In many cases, child components need to access data in the parent component `loa
|
|
185
191
|
import { useRouteLoaderData } from '@modern-js/runtime/router';
|
186
192
|
|
187
193
|
export default function UserLayout() {
|
188
|
-
// Get the data returned by the loader in routes/user/layout.
|
194
|
+
// Get the data returned by the loader in routes/user/layout.data.ts
|
189
195
|
const data = useRouteLoaderData('user/layout');
|
190
196
|
return (
|
191
197
|
<div>
|
@@ -219,12 +225,12 @@ If you want to get the data returned by the `loader` in `entry1/routes/layout.ts
|
|
219
225
|
This feature is currently experimental and the API may change in the future.
|
220
226
|
:::
|
221
227
|
|
222
|
-
Create `user/layout.
|
228
|
+
Create `user/layout.data.ts` and add the following code:
|
223
229
|
|
224
|
-
```ts title="routes/user/layout.
|
230
|
+
```ts title="routes/user/layout.data.ts"
|
225
231
|
import { defer } from '@modern-js/runtime/router';
|
226
232
|
|
227
|
-
const loader = () =>
|
233
|
+
export const loader = () =>
|
228
234
|
defer({
|
229
235
|
userInfo: new Promise(resolve => {
|
230
236
|
setTimeout(() => {
|
@@ -236,7 +242,6 @@ const loader = () =>
|
|
236
242
|
}),
|
237
243
|
});
|
238
244
|
|
239
|
-
export default loader;
|
240
245
|
```
|
241
246
|
|
242
247
|
Add the following code in `user/layout.tsx`:
|
@@ -286,24 +291,27 @@ Currently, there is no such restriction under CSR, but we strongly recommend tha
|
|
286
291
|
|
287
292
|
```ts
|
288
293
|
// This won't work!
|
289
|
-
export
|
290
|
-
|
291
|
-
|
292
|
-
method: () => {},
|
293
|
-
};
|
294
|
+
export const loader = async () => {
|
295
|
+
const res = fetch('https://api/user/profile');
|
296
|
+
return res.json();
|
294
297
|
};
|
298
|
+
|
299
|
+
import { loader } from './page.data.ts';
|
300
|
+
export default function RouteComp() {
|
301
|
+
const data = loader();
|
302
|
+
}
|
295
303
|
```
|
296
304
|
|
297
305
|
2. Modern.js will call the `loader` function for you, so you should not call the `loader` function yourself:
|
298
306
|
|
299
307
|
```tsx
|
300
308
|
// This won't work!
|
301
|
-
export
|
309
|
+
export const loader = async () => {
|
302
310
|
const res = fetch('https://api/user/profile');
|
303
311
|
return res.json();
|
304
312
|
};
|
305
313
|
|
306
|
-
import loader from './page.
|
314
|
+
import { loader } from './page.data.ts';
|
307
315
|
export default function RouteComp() {
|
308
316
|
const data = loader();
|
309
317
|
}
|
@@ -315,7 +323,7 @@ export default function RouteComp() {
|
|
315
323
|
// Not allowed
|
316
324
|
// routes/layout.tsx
|
317
325
|
import { useLoaderData } from '@modern-js/runtime/router';
|
318
|
-
import { ProfileData } from './page.
|
326
|
+
import { ProfileData } from './page.data.ts'; // should use "import type" instead
|
319
327
|
|
320
328
|
export const fetch = wrapFetch(fetch);
|
321
329
|
|
@@ -324,7 +332,7 @@ export default function UserPage() {
|
|
324
332
|
return <div>{profileData}</div>;
|
325
333
|
}
|
326
334
|
|
327
|
-
// routes/layout.
|
335
|
+
// routes/layout.data.ts
|
328
336
|
import { fetch } from './layout.tsx'; // should not be imported from the routing component
|
329
337
|
export type ProfileData = {
|
330
338
|
/* some types */
|
@@ -0,0 +1,241 @@
|
|
1
|
+
---
|
2
|
+
title: Data writing
|
3
|
+
sidebar_position: 4
|
4
|
+
---
|
5
|
+
|
6
|
+
# Data writing
|
7
|
+
|
8
|
+
In the Data Loader chapter, the way Modern.js fetch data is introduced. You may encounter two problems.:
|
9
|
+
1. How to update the data in Data Loader?
|
10
|
+
2. How to write new data to the server?
|
11
|
+
|
12
|
+
EdenX's solution for this is DataAction.
|
13
|
+
|
14
|
+
## Basic Example
|
15
|
+
|
16
|
+
Data Action, like Data Loader, is also based on convention routing. Through Modern.js's [nested routing](/guides/basic-features/routes#routing-file-convention), each routing component (`layout.ts`, `page.ts` or `$.tsx`) can have a `data` file with the same name, and a function named `action` can be exported in the `data` file.
|
17
|
+
```bash
|
18
|
+
.
|
19
|
+
└── routes
|
20
|
+
└── user
|
21
|
+
├── layout.tsx
|
22
|
+
└── layout.data.ts
|
23
|
+
```
|
24
|
+
Define the following code in the file:
|
25
|
+
```ts title="routes/user/layout.data.ts"
|
26
|
+
import type { ActionFunction } from '@modern-js/runtime/router';
|
27
|
+
|
28
|
+
export const action: ActionFunction = ({ request }) => {
|
29
|
+
const newUser = await request.json();
|
30
|
+
const name = newUser.name;
|
31
|
+
return updateUserProfile(name);
|
32
|
+
}
|
33
|
+
```
|
34
|
+
|
35
|
+
```tsx title="routes/user/layout.tsx"
|
36
|
+
import {
|
37
|
+
useFetcher,
|
38
|
+
useLoaderData,
|
39
|
+
useParams,
|
40
|
+
Outlet
|
41
|
+
} from '@modern-js/runtime/router';
|
42
|
+
|
43
|
+
export default () => {
|
44
|
+
const userInfo = useLoaderData();
|
45
|
+
const { submit } = useFetcher();
|
46
|
+
const editUser = () => {
|
47
|
+
const newUser = {
|
48
|
+
name: 'Modern.js'
|
49
|
+
}
|
50
|
+
return submit(newUser, {
|
51
|
+
method: 'post',
|
52
|
+
encType: 'application/json',
|
53
|
+
})
|
54
|
+
}
|
55
|
+
return (
|
56
|
+
<div>
|
57
|
+
<button onClick={editUser}>edit user</button>
|
58
|
+
<div className="user-profile">
|
59
|
+
{userInfo}
|
60
|
+
</div>
|
61
|
+
<Outlet context={userInfo}></Outlet>
|
62
|
+
</div>
|
63
|
+
)
|
64
|
+
}
|
65
|
+
```
|
66
|
+
|
67
|
+
Here, when the submit is executed, the defined action function will be triggered; in the action function, the submitted data can be obtained through request (request.json, request.formData, etc.), and the data can be obtained, and then the data can be sent to the server.
|
68
|
+
|
69
|
+
After the action function is executed, the loader function code will be executed and the corresponding data and views will be updated.
|
70
|
+
|
71
|
+

|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
## Why provide Data Action?
|
76
|
+
|
77
|
+
Data Action is mainly provided in Modern.js to keep the state of the UI and the server in sync, which can reduce the burden of state management.
|
78
|
+
|
79
|
+
The traditional state management method will hold the state on the client side and remotely respectively::
|
80
|
+
|
81
|
+

|
82
|
+
|
83
|
+
In Modern.js, we hope to help developers automatically synchronize the state of the client and server through Loader and Action::
|
84
|
+
|
85
|
+

|
86
|
+
|
87
|
+
If the data shared by the components in the project are the state of the main server, there is no need to introduce a client state management library in the project, request data through Data Loader, through [`useRouteLoaderData`](/guides/basic-fe Atures/data/data-fetch.md) shares data in subcomponents,
|
88
|
+
|
89
|
+
Modify and synchronize the state of the server through Data Action.
|
90
|
+
|
91
|
+
|
92
|
+
|
93
|
+
## `action` function
|
94
|
+
|
95
|
+
Like the `loader` function, the `action` function has two parameters, `params` and `request`:
|
96
|
+
|
97
|
+
### `params`
|
98
|
+
|
99
|
+
When the routing file passes through `[]`, it will be used as [dynamic routing](/guides/basic-features/routes#dynamic routing), and the dynamic routing fragment will be passed into the `action` function as a parameter::
|
100
|
+
|
101
|
+
```tsx
|
102
|
+
// routes/user/[id]/page.data.ts
|
103
|
+
import { ActionFunctionArgs } from '@modern-js/runtime/router';
|
104
|
+
|
105
|
+
export const action = async ({ params }: ActionFunctionArgs) => {
|
106
|
+
const { id } = params;
|
107
|
+
const res = await fetch(`https://api/user/${id}`);
|
108
|
+
return res.json();
|
109
|
+
};
|
110
|
+
```
|
111
|
+
|
112
|
+
When accessing `/user/123`, the parameter of the `action` function is `{ params: { id: '123' } }`.
|
113
|
+
|
114
|
+
|
115
|
+
### `request`
|
116
|
+
|
117
|
+
Through `request`, you can fetch data submitted by the client in the action function, such as `request.json()`, `request.formData()`, `request.json()`, etc.
|
118
|
+
|
119
|
+
For the specific API, please refer to [data type] (#data-type).
|
120
|
+
|
121
|
+
```tsx
|
122
|
+
// routes/user/[id]/page.data.ts
|
123
|
+
import { ActionFunctionArgs } from '@modern-js/runtime/router';
|
124
|
+
|
125
|
+
export const action = async ({ request }: ActionFunctionArgs) => {
|
126
|
+
const newUser = await request.json();
|
127
|
+
return updateUser(newUser);
|
128
|
+
};
|
129
|
+
```
|
130
|
+
|
131
|
+
### Return Value
|
132
|
+
|
133
|
+
The return value of the `action` function can be any serializable value or a [Fetch Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) instance,
|
134
|
+
|
135
|
+
The data in the response can be accessed through [`useActionData`](https://reactrouter.com/en/main/hooks/use-action-data).
|
136
|
+
|
137
|
+
|
138
|
+
## useSubmit 和 useFetcher
|
139
|
+
|
140
|
+
### Differences
|
141
|
+
|
142
|
+
You can use [`useSubmit`](https://reactrouter.com/en/main/hooks/use-submit) or [`useFetcher`](https://reactrouter.com/en/main/hooks/use-fetcher) calls action, and the difference between them is through
|
143
|
+
|
144
|
+
`useSubmit` calls action, which will trigger the browser's navigation, and `useFetcher` will not trigger the browser's navigation.
|
145
|
+
|
146
|
+
useSubmit:
|
147
|
+
|
148
|
+
```ts
|
149
|
+
const submit = useSubmit();
|
150
|
+
submit(null, { method: "post", action: "/logout" });
|
151
|
+
```
|
152
|
+
|
153
|
+
useFetcher:
|
154
|
+
```ts
|
155
|
+
const { submit } = useFetcher();
|
156
|
+
submit(null, { method: "post", action: "/logout" });
|
157
|
+
```
|
158
|
+
|
159
|
+
The `submit` function has two input parameters, `method` and `action`. `method` is equivalent to `method` at the time of form submission. In most scenarios where data is written,the `method` can be passed into `post`.
|
160
|
+
|
161
|
+
`action` is used to specify which routing component `action` is triggered. If the `action` parameter is not passed in, the action of the current routing component will be triggered by default, that is,
|
162
|
+
the execution of submit in the `user/page.tsx` component or subcomponent will trigger the action defined in `user/page.data.ts`.
|
163
|
+
|
164
|
+
|
165
|
+
:::info
|
166
|
+
For more information about these two APIs, please refer to the relevant documents:
|
167
|
+
- [`useSubmit`](https://reactrouter.com/en/main/hooks/use-submit)
|
168
|
+
- [`useFetcher`](https://reactrouter.com/en/main/hooks/use-fetcher)
|
169
|
+
|
170
|
+
:::
|
171
|
+
|
172
|
+
|
173
|
+
### Type of data
|
174
|
+
|
175
|
+
The first parameter of the `submit` function can accept different types of values.
|
176
|
+
Such as `FormData`:
|
177
|
+
```ts
|
178
|
+
let formData = new FormData();
|
179
|
+
formData.append("cheese", "gouda");
|
180
|
+
submit(formData);
|
181
|
+
// In the action, you can get the data by request.json
|
182
|
+
```
|
183
|
+
|
184
|
+
Or the value of type `URLSearchParams`:
|
185
|
+
```ts
|
186
|
+
let searchParams = new URLSearchParams();
|
187
|
+
searchParams.append("cheese", "gouda");
|
188
|
+
submit(searchParams);
|
189
|
+
// In the action, you can get the data by request.json
|
190
|
+
```
|
191
|
+
|
192
|
+
Or any acceptable value of the `URLSearchParams` constructor:
|
193
|
+
```ts
|
194
|
+
submit("cheese=gouda&toasted=yes");
|
195
|
+
submit([
|
196
|
+
["cheese", "gouda"],
|
197
|
+
["toasted", "yes"],
|
198
|
+
]);
|
199
|
+
// In the action, you can get the data by request.json
|
200
|
+
```
|
201
|
+
|
202
|
+
By default, if the first parameter in the `submit` function is an object, the corresponding data will be encoded as `formData`:
|
203
|
+
|
204
|
+
```ts
|
205
|
+
submit(
|
206
|
+
{ key: "value" },
|
207
|
+
{
|
208
|
+
method: "post",
|
209
|
+
encType: "application/x-www-form-urlencoded",
|
210
|
+
}
|
211
|
+
);
|
212
|
+
|
213
|
+
// In the action, you can get the data by request.formData
|
214
|
+
```
|
215
|
+
|
216
|
+
it can also be specified as json encoding:
|
217
|
+
|
218
|
+
```tsx
|
219
|
+
submit(
|
220
|
+
{ key: "value" },
|
221
|
+
{ method: "post", encType: "application/json" }
|
222
|
+
);
|
223
|
+
|
224
|
+
submit('{"key":"value"}', {
|
225
|
+
method: "post",
|
226
|
+
encType: "application/json",
|
227
|
+
});
|
228
|
+
|
229
|
+
// In the action, you can get the data by request.json
|
230
|
+
```
|
231
|
+
|
232
|
+
or submit plain text:
|
233
|
+
```ts
|
234
|
+
submit("value", { method: "post", encType: "text/plain" });
|
235
|
+
// In the action, you can get the data by request.text
|
236
|
+
```
|
237
|
+
|
238
|
+
|
239
|
+
## CSR 和 SSR
|
240
|
+
|
241
|
+
Like Data Loader, in the SSR project, Data Action is executed on the server (the framework will automatically send a request to trigger Data Action), while in the CSR project, Data Action is executed on the client.
|
@@ -161,7 +161,7 @@ These properties as defined are available via the [`useMatches`](https://reactro
|
|
161
161
|
|
162
162
|
```ts title="routes/layout.ts"
|
163
163
|
export default () => {
|
164
|
-
const matches = useMatches;
|
164
|
+
const matches = useMatches();
|
165
165
|
const breadcrumbs = matches.map(
|
166
166
|
matchedRoute => matchedRoute?.handle?.breadcrumbName,
|
167
167
|
);
|
@@ -186,7 +186,7 @@ The `routes/[id]/page.tsx` file will be converted to the `/:id` route. Except fo
|
|
186
186
|
|
187
187
|
In the component, you can use [useParams](/apis/app/runtime/router/router#useparams) to get the corresponding named parameter.
|
188
188
|
|
189
|
-
In the loader, params will be passed as the input parameter of the [loader function](/guides/basic-features/data-fetch#loader-function), and you can get the parameter value through `params.xxx`.
|
189
|
+
In the loader, params will be passed as the input parameter of the [loader function](/guides/basic-features/data/data-fetch#loader-function), and you can get the parameter value through `params.xxx`.
|
190
190
|
|
191
191
|
### Dynamic Optional Routing
|
192
192
|
|
@@ -206,7 +206,7 @@ The `routes/user/[id$]/page.tsx` file will be converted to the `/user/:id?` rout
|
|
206
206
|
|
207
207
|
In the component, you can use [useParams](/apis/app/runtime/router/router#useparams) to get the corresponding named parameter.
|
208
208
|
|
209
|
-
In the loader, params will be passed as the input parameter of the [loader function](/guides/basic-features/data-fetch#loader-function), and you can get the parameter value through `params.xxx`.
|
209
|
+
In the loader, params will be passed as the input parameter of the [loader function](/guides/basic-features/data/data-fetch#loader-function), and you can get the parameter value through `params.xxx`.
|
210
210
|
|
211
211
|
### Catch-all Routing
|
212
212
|
|
@@ -351,12 +351,12 @@ Similarly, when the route jumps from `/` or `/blog` to `/blog/123`, if the JS Ch
|
|
351
351
|
|
352
352
|
### Redirect
|
353
353
|
|
354
|
-
You can use a [Data Loader](/guides/basic-features/data-fetch) file to redirect a route. For example, if you have a `routes/user/page.tsx` file and want to redirect the corresponding route, you can create a `routes/user/page.
|
354
|
+
You can use a [Data Loader](/guides/basic-features/data/data-fetch) file to redirect a route. For example, if you have a `routes/user/page.tsx` file and want to redirect the corresponding route, you can create a `routes/user/page.data.ts` file:
|
355
355
|
|
356
|
-
```ts title="routes/user/page.
|
356
|
+
```ts title="routes/user/page.data.ts"
|
357
357
|
import { redirect } from '@modern-js/runtime/router';
|
358
358
|
|
359
|
-
export
|
359
|
+
export const loader = () => {
|
360
360
|
const user = await getUser();
|
361
361
|
if (!user) {
|
362
362
|
return redirect('/login');
|
@@ -502,7 +502,7 @@ To further improve the user experience and reduce loading time, Modern.js suppor
|
|
502
502
|
:::info
|
503
503
|
|
504
504
|
- This feature is currently only supported in Webpack projects and not yet supported in Rspack projects.
|
505
|
-
- Preloading data currently only preloads the data returned by the [Data Loader](/guides/basic-features/data-fetch) in SSR projects.
|
505
|
+
- Preloading data currently only preloads the data returned by the [Data Loader](/guides/basic-features/data/data-fetch) in SSR projects.
|
506
506
|
|
507
507
|
:::
|
508
508
|
|
@@ -18,7 +18,7 @@ pnpm add faker@5
|
|
18
18
|
pnpm add @types/faker@5 -D
|
19
19
|
```
|
20
20
|
|
21
|
-
Create `src/routes/page.
|
21
|
+
Create `src/routes/page.data.ts`:
|
22
22
|
|
23
23
|
```tsx
|
24
24
|
import { name, internet } from 'faker';
|
@@ -32,7 +32,7 @@ type LoaderData = {
|
|
32
32
|
}[];
|
33
33
|
};
|
34
34
|
|
35
|
-
export
|
35
|
+
export const loader = async (): Promise<LoaderData> => {
|
36
36
|
const data = new Array(20).fill(0).map(() => {
|
37
37
|
const firstName = name.firstName();
|
38
38
|
return {
|
@@ -195,9 +195,9 @@ const Item = ({
|
|
195
195
|
export default Item;
|
196
196
|
```
|
197
197
|
|
198
|
-
Next, we add `src/routes.page.
|
198
|
+
Next, we add `src/routes.page.data` and modify `src/routes/page.tsx` to pass more parameters to the `<Item>` component:
|
199
199
|
|
200
|
-
```tsx title="src/routes/page.
|
200
|
+
```tsx title="src/routes/page.data.ts"
|
201
201
|
export type LoaderData = {
|
202
202
|
code: number;
|
203
203
|
data: {
|
@@ -207,7 +207,7 @@ export type LoaderData = {
|
|
207
207
|
}[];
|
208
208
|
};
|
209
209
|
|
210
|
-
export
|
210
|
+
export const loader = async (): Promise<LoaderData> => {
|
211
211
|
const data = new Array(20).fill(0).map(() => {
|
212
212
|
const firstName = name.firstName();
|
213
213
|
return {
|
@@ -233,7 +233,7 @@ import { List } from 'antd';
|
|
233
233
|
import { name, internet } from 'faker';
|
234
234
|
import Item from '../components/Item';
|
235
235
|
import contacts from '../models/contacts';
|
236
|
-
import type { LoaderData } from './page.
|
236
|
+
import type { LoaderData } from './page.data';
|
237
237
|
|
238
238
|
function Index() {
|
239
239
|
const { data } = useLoaderData() as LoaderData;
|