Skip to content

Commit 46b33c9

Browse files
authored
add useSubmission and useSubmissions (#917)
1 parent 4cfd825 commit 46b33c9

File tree

3 files changed

+384
-1
lines changed

3 files changed

+384
-1
lines changed

src/routes/solid-router/reference/data-apis/data.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
"create-async.mdx",
77
"create-async-store.mdx",
88
"query.mdx",
9-
"response-helpers.mdx"
9+
"response-helpers.mdx",
10+
"use-submission.mdx",
11+
"use-submissions.mdx"
1012
]
1113
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
---
2+
title: useSubmission
3+
---
4+
5+
This helper is used to handle form submissions and can provide optimistic updates while actions are in flight as well as pending state feedback.
6+
This method will return a single (latest) value while its sibling, [`useSubmissions`](/solid-router/reference/data-apis/use-submissions), will return all values submitted while the component is active. With an optional second parameter for a filter function.
7+
8+
It's important to note that `useSubmission` requires the form method to be **post** otherwise it will trigger a browser navigation and will not work.
9+
10+
```tsx title="component.tsx" {4,8}
11+
import { useSubmission } from "@solidjs/router";
12+
13+
function Component() {
14+
const submission = useSubmission(postNameAction);
15+
16+
return (
17+
<form action={postNameAction} method="post">
18+
<input type="text" name="name" />
19+
<button type="submit">
20+
{submission.pending ? "Adding..." : "Add"}
21+
</button>
22+
</form>
23+
)
24+
}
25+
```
26+
27+
## Creating the action
28+
29+
The Action which will trigger the submission should be created with the [`action()`](/solid-router/reference/data-apis/action) helper and, when in a [SolidStart](/solid-start) app, it is recommended to leverage the `"use server"` directive to leverage the caching and RPC capabilities from the server-side.
30+
31+
```tsx title="/component.tsx" {1,3-4}
32+
import { action } from "@solidjs/router";
33+
34+
const postNameAction = action(() => {
35+
"use server";
36+
/*... logic ...*/
37+
return { data: "Hello SolidStart" };
38+
})
39+
40+
```
41+
42+
## Filtering Submissions
43+
44+
As an optional second parameter, the `useSubmission` helper can receive a filter function to only return the submission that matches the condition.
45+
The filter receives the submitted dated as a parameter and should return a boolean value.
46+
E.g.: action below will only submit if the name is "solid".
47+
48+
```tsx title="component.tsx" {4-8}
49+
import { useSubmission } from "@solidjs/router";
50+
51+
function Component() {
52+
const submission = useSubmission(postNameAction, ([formData]) => {
53+
const name = formData.get("name") ?? "";
54+
55+
return name === "solid";
56+
});
57+
58+
return (
59+
<form action={postNameAction} method="post">
60+
<input type="text" name="name" />
61+
<button type="submit">
62+
{submission.pending ? "Adding..." : "Add"}
63+
</button>
64+
</form>
65+
)
66+
}
67+
```
68+
69+
## Optimistic Updates
70+
71+
When the form is submitted, the `submission` object will be updated with the new value and the `pending` property will be set to `true`.
72+
This allows you to provide feedback to the user that the action is in progress.
73+
Once the action is complete, the `pending` property will be set to `false` and the `result` property will be updated with final value.
74+
75+
76+
<TabsCodeBlocks>
77+
<div id="ts">
78+
```tsx title="component.tsx" {5,9-11}
79+
import { Show } from "solid-js";
80+
import { useSubmission } from "@solidjs/router";
81+
82+
function Component() {
83+
const submission = useSubmission(postNameAction);
84+
85+
return (
86+
<>
87+
<Show when={submission.input?.[0].get("name")}>
88+
{(name) => <div>Optimistic: {name() as string}</div>}
89+
</Show>
90+
91+
<Show when={submission.result?.name}>
92+
{(name) => <div>Result: {name()}</div>}
93+
</Show>
94+
95+
<form method="post" action={sendData}>
96+
<input type="text" name="name" required />
97+
<button type="submit" disabled={submission.pending}>
98+
{submission.pending ? "Submitting" : "Submit"}
99+
</button>
100+
</form>
101+
</>
102+
)
103+
}
104+
```
105+
</div>
106+
<div id="js">
107+
```tsx title="component.jsx" {5,9-11}
108+
import { Show } from "solid-js";
109+
import { useSubmission } from "@solidjs/router";
110+
111+
function Component() {
112+
const submission = useSubmission(postNameAction);
113+
114+
return (
115+
<>
116+
<Show when={submission.input?.[0].get("name")}>
117+
{(name) => <div>Optimistic: {name()}</div>}
118+
</Show>
119+
120+
<Show when={submission.result?.name}>
121+
{(name) => <div>Result: {name()}</div>}
122+
</Show>
123+
124+
<form method="post" action={sendData}>
125+
<input type="text" name="name" required />
126+
<button type="submit" disabled={submission.pending}>
127+
{submission.pending ? "Submitting" : "Submit"}
128+
</button>
129+
</form>
130+
</>
131+
)
132+
}
133+
```
134+
</div>
135+
</TabsCodeBlocks>
136+
137+
## Error Handling
138+
139+
If the action fails, the `submission` object will be updated with the error and the `pending` property will be set to `false`.
140+
This allows you to provide feedback to the user that the action has failed. Additionally, the return type of `useSubmission` will have a new key `error` that will contain the error object thrown by the submission handler.
141+
142+
At this stage, you can also use the `retry()` method to attempt the action again or the `clear()` to wipe the filled data in the platform.
143+
144+
```tsx title="component.tsx" {12-18}
145+
import { Show } from "solid-js";
146+
import { useSubmission } from "@solidjs/router";
147+
148+
function Component() {
149+
const submission = useSubmission(postNameAction);
150+
151+
return (
152+
<>
153+
<Show when={submit.error}>
154+
{(error) => (
155+
<div>
156+
<p>Error: {error.message}</p>
157+
<button onClick={() => submit.clear()}>
158+
Clear
159+
</button>
160+
<button onClick={async () => submit.retry()}>
161+
Retry
162+
</button>
163+
</div>
164+
)}
165+
</Show>
166+
167+
<form method="post" action={sendData}>
168+
<input type="text" name="name" required />
169+
<button type="submit" disabled={submission.pending}>
170+
{submission.pending ? "Submitting" : "Submit"}
171+
</button>
172+
</form>
173+
</>
174+
)
175+
}
176+
```
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
---
2+
title: useSubmissions
3+
---
4+
5+
This helper is used to handle form submissions and can provide optimistic updates while actions are in flight as well as pending state feedback.
6+
This method will return an iterable of all submitted actions while its component is mounted. With an optional second parameter for a filter function.
7+
8+
<Callout type="tip">
9+
If you only care for the latest submission, you can use the [`useSubmission`](/solid-router/reference/data-apis/use-submission) helper.
10+
</Callout>
11+
12+
It's important to note that it requires the form method to be **post** otherwise it will trigger a browser navigation and will not work.
13+
14+
In the example below, the `useSubmissions` helper is used to retain a list of all submission results to that action while also giving feedback on the pending state of the current in-flight submission.
15+
16+
```tsx title="component.tsx" {4,9-20, 23}
17+
import { useSubmissions } from "@solidjs/router";
18+
19+
function Component() {
20+
const submissions = useSubmissions(postNameAction);
21+
22+
return (
23+
<form method="post" action={postNameAction}>
24+
<ul>
25+
<For each={Array.from(submissions.entries())}>
26+
{([attemptIndex, data]) => (
27+
<Show when={data.result}>
28+
{ result => (
29+
<li>
30+
Backend {attemptIndex}: {result.name}
31+
</li>
32+
)}
33+
</Show>
34+
</>
35+
)}
36+
</For>
37+
</ul>
38+
<input name="name" type="text" />
39+
<button type="submit">{submissions.pending ? "sending" : "send"}</button>
40+
</form>
41+
)
42+
}
43+
```
44+
45+
## Creating the action
46+
47+
The Action which will trigger the submission should be created with the [`action()`](/solid-router/reference/data-apis/action) helper and, when in a [SolidStart](/solid-start) app. If in a [SolidStart](/solid-start) app to leverage the caching and RPC capabilities from the server-side.
48+
49+
```tsx title="/component.tsx" {1,3-4}
50+
import { action } from "@solidjs/router";
51+
52+
const postNameAction = action(() => {
53+
"use server";
54+
/*... logic ...*/
55+
return { data: "Hello SolidStart" };
56+
})
57+
58+
```
59+
60+
## Filtering Submissions
61+
62+
As an optional second parameter, the `useSubmissions` helper can receive a filter function to only return the submission that matches the condition.
63+
The filter receives the submitted dated as a parameter and should return a boolean value.
64+
E.g.: action below will only submit if the name is "solid".
65+
66+
```tsx title="component.tsx" {4-8}
67+
import { useSubmissions } from "@solidjs/router";
68+
69+
function Component() {
70+
const submissions = useSubmissions(postNameAction, ([formData]) => {
71+
const name = formData.get("name") ?? "";
72+
73+
return name === "solid";
74+
});
75+
76+
return (
77+
<form method="post" action={postNameAction}>
78+
<ul>
79+
<For each={Array.from(submissions.entries())}>
80+
{([attemptIndex, data]) => (
81+
<Show when={data.result}>
82+
{ result => (
83+
<li>
84+
Backend {attemptIndex}: {result.name}
85+
</li>
86+
)}
87+
</Show>
88+
</>
89+
)}
90+
</For>
91+
</ul>
92+
<input name="name" type="text" />
93+
<button type="submit">{submissions.pending ? "sending" : "send"}</button>
94+
</form>
95+
)
96+
}
97+
```
98+
99+
## Optimistic Updates
100+
101+
When the form is submitted, the `submission` object will be updated with the new value and the `pending` property will be set to `true`.
102+
This allows you to provide feedback to the user that the action is in progress.
103+
Once the action is complete, the `pending` property will be set to `false` and the `result` property will be updated with final value.
104+
105+
106+
<TabsCodeBlocks>
107+
<div id="ts">
108+
```tsx title="component.tsx" {5,12-19}
109+
import { Show } from "solid-js";
110+
import { useSubmissions } from "@solidjs/router";
111+
112+
function Component() {
113+
const submissions = useSubmissions(postNameAction);
114+
115+
return (
116+
<form method="post" action={postNameAction}>
117+
<ul>
118+
<For each={Array.from(submissions.entries())}>
119+
{([attemptIndex, data]) => (
120+
<Show when={data.input[0].entries().next()}>
121+
{(input) => {
122+
const name = (input().value as [string, string])[1]
123+
124+
return (
125+
<li>Optimistic: {name}</li>
126+
)}}
127+
</Show>
128+
)}
129+
</For>
130+
</ul>
131+
<input name="name" type="text" />
132+
<button type="submit">{submissions.pending ? "sending" : "send"}</button>
133+
</form>
134+
)
135+
}
136+
```
137+
</div>
138+
<div id="js">
139+
```tsx title="component.jsx" {5,12-19}
140+
import { Show } from "solid-js";
141+
import { useSubmissions } from "@solidjs/router";
142+
143+
function Component() {
144+
const submissions = useSubmissions(postNameAction);
145+
146+
return (
147+
<form method="post" action={postNameAction}>
148+
<ul>
149+
<For each={Array.from(submissions.entries())}>
150+
{([attemptIndex, data]) => (
151+
<Show when={data.input[0].entries().next()}>
152+
{(input) => {
153+
const name = input().value[1]
154+
155+
return (
156+
<li>Optimistic: {name}</li>
157+
)}}
158+
</Show>
159+
)}
160+
</For>
161+
</ul>
162+
<input name="name" type="text" />
163+
<button type="submit">{submissions.pending ? "sending" : "send"}</button>
164+
</form>
165+
)
166+
}
167+
```
168+
</div>
169+
</TabsCodeBlocks>
170+
171+
## Error Handling
172+
173+
If the action fails, the `submission` object will be updated with the error and the `pending` property will be set to `false`.
174+
This allows you to provide feedback to the user that the action has failed. Additionally, the return type of `useSubmission` will have a new key `error` that will contain the error object thrown by the submission handler.
175+
176+
At this stage, you can also use the `retry()` method to attempt the action again or the `clear()` to wipe the filled data in the platform.
177+
178+
```tsx title="component.tsx" {12-18}
179+
import { Show } from "solid-js";
180+
import { useSubmissions } from "@solidjs/router";
181+
182+
function Component() {
183+
const submissions = useSubmissions(postNameAction);
184+
185+
return (
186+
<form method="post" action={postNameAction}>
187+
<ul>
188+
<For each={Array.from(submissions.entries())}>
189+
{([attempt, data]) => (
190+
<Show when={data.error}>
191+
<li>
192+
<p>Backend {attempt}: {data.error.message}</p>
193+
<button onClick={() => data.retry()}>retry</button>
194+
<button onClick={() => data.clear()}>clear</button>
195+
</li>
196+
</Show>
197+
)}
198+
</For>
199+
</ul>
200+
<input name="name" type="text" required autocomplete="off" />
201+
<button type="submit">{submissions.pending ? "sending" : "send"}</button>
202+
</form>
203+
)
204+
}
205+
```

0 commit comments

Comments
 (0)