Skip to content

Commit af8efef

Browse files
committed
update
1 parent 9f08b53 commit af8efef

File tree

3 files changed

+126
-111
lines changed

3 files changed

+126
-111
lines changed

src/routes/solid-router/concepts/actions.mdx

Lines changed: 123 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -2,151 +2,166 @@
22
title: "Actions"
33
---
44

5-
When developing applications, it is common to need to communicate new information to the server based on user interactions.
6-
Actions are Solid Router’s solution to this problem.
5+
Actions provide a powerful and flexible mechanism for handling data mutations and side effects.
6+
They are designed to simplify your application's data flow, ensure a consistent user experience, and integrate seamlessly with Solid's reactivity.
77

8-
## What are actions?
8+
Actions provide several key benefits:
99

10-
Actions are asynchronous processing functions that allow you to submit data to your server and receive a response.
11-
They are isomorphic, meaning they can run either on the server or the client, depending on what is needed.
12-
This flexibility makes actions a powerful tool for managing and tracking data submissions.
13-
14-
### How actions work
15-
16-
Actions represent the server-side part of an [HTML form](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
17-
They handle submissions through POST requests, allowing you to easily use HTML forms to send data.
18-
19-
When a user performs an action, such as submitting a form, the data is sent to the server for processing via an action.
20-
21-
### Benefits of using actions
22-
23-
1. **Isomorphic**: Since actions can run on both the server and client, you can optimize performance by choosing the best execution environment for your needs.
24-
2. **Asynchronous processing**: Actions handle data submissions asynchronously, ensuring that your application remains responsive.
25-
3. **Simplified data handling**: By using actions, the process of managing and tracking data submissions can be streamlined, reducing the complexity of your application.
10+
- **Centralized Logic**: Encapsulate the logic for data modifications in a single, reusable function.
11+
- **Integrated State Management**: Solid Router automatically tracks the execution state of an action (whether it's pending, successful, or has encountered an error), making it easy to build reactive UI feedback.
12+
- **Automatic Data Revalidation**: By default, after an action successfully completes, Solid Router revalidates any queries on the same page.
13+
This ensures your UI reflects the latest data without manual intervention.
14+
- **Progressive Enhancement**: When used with HTML forms, actions can enable forms to function even if JavaScript is not yet loaded, providing a robust and accessible user experience.
2615

2716
## Creating actions
2817

29-
To create an action, use the `action` function from the `@solidjs/router` package.
30-
This function takes an asynchronous function as an argument and returns a new function that can be used to submit data.
18+
At their core, actions are **asynchronous functions** that you define using the `action` function.
19+
The [`action`](/solid-router/reference/data-apis/action) function takes your asynchronous function and returns an action object.
20+
21+
To define an action, import the `action` function from `@solidjs/router` and pass it your asynchronous logic:
3122

3223
```tsx
3324
import { action } from "@solidjs/router";
3425

35-
const echo = action(async (message: string) => {
36-
// Simulates an asynchronous operation, such as an API call
37-
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
38-
console.log(message);
26+
const addPostAction = action(async (title: string) => {
27+
const response = await fetch("https://api.com/posts", {
28+
method: "POST",
29+
headers: {
30+
"Content-Type": "application/json",
31+
},
32+
body: JSON.stringify({ title }),
33+
});
34+
35+
if (!response.ok) {
36+
const errorData = await response.json();
37+
throw new Error(errorData.message || "Failed to add post");
38+
}
39+
40+
return await response.json();
3941
});
4042
```
4143

42-
In this example, the `echo` action simulates a fetch call with a 1 second delay before logging the message to the console.
43-
The `echo` action will act as a backend, however, it can be substituted for any API provided it can be run on the client.
44-
Typically, route actions are used with some sort of solution like fetch or GraphQL.
44+
In this example, `addPostAction` handles sending a POST request to create a new post.
45+
The return value of the action can be accessed later when tracking action state.
4546

46-
:::tip
47-
In [SolidStart](/solid-start) apps, it's recommended to use the [`"use server"`](/solid-start/reference/server/use-server) directive to leverage server-side functionality.
47+
:::note[Server-Side Rendering (SSR)]
48+
When using actions with SSR, you must provide a unique name string as the second parameter to the `action` function.
49+
This is crucial for Solid Router to correctly identify and re-run actions on the server.
50+
We'll explore this in more detail in the Handling Form Submissions section.
4851
:::
4952

50-
### Using actions
51-
52-
To use the action, you can call it from within a component using [`useAction`](/solid-router/reference/data-apis/use-action).
53-
This returns a function that can be called with the necessary arguments to trigger the action.
54-
55-
```tsx del={1} ins={2,9-13}
56-
import { action } from "@solidjs/router";
57-
import { action, useAction } from "@solidjs/router";
58-
59-
const echo = action(async (message: string) => {
60-
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
61-
console.log(message);
62-
});
63-
64-
export function MyComponent() {
65-
const myEcho = useAction(echo);
66-
67-
myEcho("Hello from Solid!");
68-
}
69-
```
70-
71-
In this component, `useAction` is used to get a reference to the `echo` action.
72-
The action is then called with the message `"Hello from Solid!"`, which will be logged to the console after a 1 second delay.
73-
74-
### Returning data from actions
53+
## How to Use Actions
7554

76-
In many cases, after submitting data, the server sends some data back as well.
77-
This may be in the form of an error message if something has failed or the results of a successful operation.
78-
Anything returned from an action can be accessed using the reactive `action.result` property, where the value can change each time you submit your action.
55+
Solid Router offers two primary ways to invoke an action:
7956

80-
To access the action's result, you must pass the action to `useSubmission`:
57+
1. **Via the `<form>` element's `action` prop**: This is the recommended approach for most data mutations, especially those triggered by user input, as it provides **progressive enhancement**.
58+
2. **Programmatically with `useAction`**: For scenarios where you need to trigger an action outside of a form context.
8159

82-
```tsx del={1} ins={2,11,15-17}
83-
import { action, useAction } from "@solidjs/router";
84-
import { action, useAction, useSubmission } from "@solidjs/router";
60+
### Handling Form Submissions with the `action` prop
8561

86-
const echo = action(async (message: string) => {
87-
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
88-
return message;
89-
});
62+
Solid Router extends the standard HTML `<form>` element to accept an `action` prop, allowing you to handle your form submissions to an action.
63+
This method provides the best user experience due to progressive enhancement.
9064

91-
export function MyComponent() {
92-
const myEcho = useAction(echo);
93-
const echoing = useSubmission(echo);
65+
When using actions with `<form>`:
9466

95-
myEcho("Hello from solid!");
67+
1. The `<form>` element **must** have `method="post"`.
68+
2. The action function will automatically receive the form's data as a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object as its first parameter.
69+
3. For SSR environments, you **must** provide a unique name string as the second parameter to the `action` function.
70+
This name is used by Solid Router to uniquely identify and serialize the action across the client and server.
9671

97-
setTimeout(() => myEcho("This is a second submission!"), 1500);
72+
```tsx
73+
import { action } from "@solidjs/router";
9874

99-
return <p>{echoing.result}</p>;
75+
const addPostAction = action(async (formData: FormData) => {
76+
const title = formData.get("title")?.toString();
77+
78+
if (!title || title.trim() === "") {
79+
throw new Error("Post title cannot be empty.");
80+
}
81+
82+
const response = await fetch("https://api.com/posts", {
83+
method: "POST",
84+
headers: {
85+
"Content-Type": "application/json",
86+
},
87+
body: JSON.stringify({ title }),
88+
});
89+
90+
if (!response.ok) {
91+
const errorData = await response.json();
92+
throw new Error(errorData.message || "Failed to add post");
93+
}
94+
}, "add-post");
95+
96+
function AddPostForm() {
97+
return (
98+
<form action={addPostAction} method="post">
99+
<label for="title">Post Title:</label>
100+
<input id="title" name="title" placeholder="Enter post title" />
101+
<button type="submit">Create Post</button>
102+
</form>
103+
);
100104
}
101105
```
102106

103-
Using `useSubmission` leaves the implementation details of how you trigger `echo` up to you.
104-
When handling user inputs, for example, it is better to use a `form` for a multitude of reasons.
107+
When this form is submitted, `addPostFormAction` will be invoked with the `FormData` containing the form values.
105108

106-
## Using forms to submit data
109+
:::tip[File Uploads]
107110

108-
When submitting data with actions, it is recommended to use HTML forms.
109-
These forms can be used prior to JavaScript loading, which creates instantly interactive applications.
110-
This also inherently provides accessibility benefits, saving the time of designing a custom UI library that may not have these benefits.
111+
If your form includes file inputs, ensure your <form> element has enctype="multipart/form-data" to correctly send the file data.
111112

112-
When using forms to submit actions, the first argument passed to your action function is an instance of [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData).
113-
To use actions with forms, pass the action to the `action` property of your form.
114-
This creates progressively enhanced forms that work even when JavaScript is disabled.
113+
```tsx
114+
<form action={uploadFileAction} method="post" enctype="multipart/form-data">
115+
<input type="file" name="myFile" />
116+
<button type="submit">Upload</button>
117+
</form>
118+
```
115119

116-
If you do not return a `Response` from your action, the user will stay on the same page and responses will be re-triggered.
117-
Using a `redirect` can tell the browser to navigate to a new page.
120+
:::
118121

122+
#### Passing additional data
119123

120-
```tsx
121-
import { action, redirect } from "@solidjs/router";
124+
Sometimes, your action might need additional data that isn't part of the form's inputs.
125+
You can pass these additional arguments using the `with` method on your action.
122126

123-
const isAdmin = action(async (formData: FormData) => {
124-
await new Promise((resolve, reject) => setTimeout(resolve, 1000));
127+
Arguments passed to `with` will be forwarded to your action function before the `FormData` object.
125128

126-
const username = formData.get("username");
129+
```tsx
130+
import { action } from "@solidjs/router";
127131

128-
if (username === "admin") throw redirect("/admin");
129-
return new Error("Invalid username");
132+
const updatePostAction = action(async (postId: string, formData: FormData) => {
133+
const newTitle = formData.get("title")?.toString();
134+
135+
if (!newTitle || newTitle.trim() === "") {
136+
throw new Error("Post title cannot be empty.");
137+
}
138+
139+
const response = await fetch(
140+
`https://api.com/posts/${encodeURIComponent(newTitle)}`,
141+
{
142+
method: "PUT",
143+
headers: {
144+
"Content-Type": "application/json",
145+
},
146+
body: JSON.stringify({ title: newTitle }),
147+
}
148+
);
149+
150+
if (!response.ok) {
151+
const errorData = await response.json();
152+
throw new Error(errorData.message || "Failed to update post");
153+
}
130154
});
131155

132-
export function MyComponent() {
133-
134-
return (
135-
<form action={isAdmin} method="post">
136-
<label for="username">Username:</label>
137-
<input type="text" name="username" />
138-
<input type="submit" value="submit" />
139-
</form>
140-
);
156+
function PostEditForm(props: { postId: string }) {
157+
return (
158+
<form action={updatePostAction.with(props.postId)} method="post">
159+
<label for="title">Title:</label>
160+
<input id="title" name="title" placeholder="New title" />
161+
<button type="submit">Update Post</button>
162+
</form>
163+
);
141164
}
142165
```
143166

144-
**Note:** If you are uploading files make sure you include `enctype="multipart/form-data"` to your `<form>` element.
145-
146-
## Error handling
147-
148-
Rather than throwing errors, it is recommended to return them from actions.
149-
This helps with the typing of submissions that would be used with `useSubmission`.
150-
This is important when handling progressive enhancement where no JavaScript is present in the client, so that errors can be used declaratively to render the updated page on the server.
151-
152-
Additionally, when using server actions, it is good practice to handle errors on the server to sanitize error messages.
167+
Here, `updatePostAction` receives `postId` (passed via `with`), and then the `formData` from the form.

src/routes/solid-router/reference/data-apis/use-submission.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const addTodoAction = action(async (formData: FormData) => {
1313
if (name.length <= 2) {
1414
throw new Error("Name must be larger than 2 characters");
1515
}
16-
}, "addTodo");
16+
}, "add-todo");
1717

1818
function AddTodoForm() {
1919
const submission = useSubmission(addTodoAction);

src/routes/solid-router/reference/data-apis/use-submissions.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const addTodoAction = action(async (formData: FormData) => {
1414
throw new Error("Name must be larger than 2 characters");
1515
}
1616
return name;
17-
}, "addTodo");
17+
}, "add-todo");
1818

1919
export default function AddTodoForm() {
2020
const submissions = useSubmissions(addTodoAction);
@@ -50,7 +50,7 @@ export default function AddTodoForm() {
5050
}
5151
```
5252

53-
:::info[Note]
53+
:::note
5454
To access the state of the most recent submission, `useSubmission`[/solid-router/reference/data-apis/use-submission] can be used.
5555
:::
5656

0 commit comments

Comments
 (0)