@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.
@@ -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.loader.ts"
26
- export default () => {
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.loader.ts"
161
+ ```tsx title="page.data.ts"
162
162
  import { useLoaderData } from '@modern-js/runtime/router';
163
163
 
164
- export default async () => {
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.loader.ts`.
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.loader.tsx"
320
+ ```ts title="routes/page.data.tsx"
321
321
  import fse from 'fs-extra';
322
- export default () => {
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.loader.ts"
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 default ({ params }: LoaderFunctionArgs) => {
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.loader.ts"
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.loader';
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.loader'`;
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.loader.ts"
507
+ ```ts title="page.data.ts"
508
508
  import { defer } from '@modern-js/runtime/router';
509
509
 
510
- export default () => {
510
+ export const loader = () => {
511
511
  const data = new Promise((resolve, reject) => {
512
512
  setTimeout(() => {
513
513
  reject(new Error('error occurs'));
@@ -0,0 +1,4 @@
1
+ {
2
+ "label": "Data solution",
3
+ "position": 3
4
+ }
@@ -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 `loader` 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:
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.loader.ts
37
+ ├── layout.data.ts
32
38
  ├── page.tsx
33
- └── page.loader.ts
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.loader.ts';
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.loader.ts"
54
+ ```ts title="routes/user/page.data.ts"
49
55
  export type ProfileData = {
50
56
  /* some types */
51
57
  };
52
58
 
53
- export default async (): Promise<ProfileData> => {
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 `loader` files share a type, so the `import type` syntax should be used.
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.loader.tsx
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.loader.ts
109
+ // routes/user/[id]/page.data.ts
104
110
  import { LoaderFunctionArgs } from '@modern-js/runtime/router';
105
111
 
106
- export default async ({ request }: LoaderFunctionArgs) => {
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.loader.tsx
156
- export default async function loader() {
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.loader.ts
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.loader.ts` and add the following code:
228
+ Create `user/layout.data.ts` and add the following code:
223
229
 
224
- ```ts title="routes/user/layout.loader.ts"
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 default () => {
290
- return {
291
- user: {},
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 default async () => {
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.loader.ts';
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.loader.ts'; // should use "import type" instead
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.loader.ts
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
+ ![action flow](https://lf3-static.bytednsdoc.com/obj/eden-cn/ulkl/ljhwZthlaukjlkulzlp/action-flow.png)
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
+ ![traditional state manage](https://lf3-static.bytednsdoc.com/obj/eden-cn/ulkl/ljhwZthlaukjlkulzlp/action-state-manage.png)
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
+ ![state manage](https://lf3-static.bytednsdoc.com/obj/eden-cn/ulkl/ljhwZthlaukjlkulzlp/action-state-manage1.png)
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.loader.ts` file:
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.loader.ts"
356
+ ```ts title="routes/user/page.data.ts"
357
357
  import { redirect } from '@modern-js/runtime/router';
358
358
 
359
- export default () => {
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.loader.ts`:
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 default async (): Promise<LoaderData> => {
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.loader` and modify `src/routes/page.tsx` to pass more parameters to the `<Item>` component:
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.loader.ts"
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 default async (): Promise<LoaderData> => {
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.loader';
236
+ import type { LoaderData } from './page.data';
237
237
 
238
238
  function Index() {
239
239
  const { data } = useLoaderData() as LoaderData;