Skip to content

Fix useSubmission/useSubmissions and action docs #1179

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 61 additions & 141 deletions src/routes/solid-router/reference/data-apis/use-submission.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,160 +2,80 @@
title: useSubmission
---

This helper is used to handle form submissions and can provide optimistic updates while actions are in flight as well as pending state feedback.
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.
The `useSubmission` function returns a submission object for a specified action.
This submission object contains properties to access the state of action execution and functions to control the action.

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.

```tsx title="component.tsx" {4,8}
import { useSubmission } from "@solidjs/router";

function Component() {
const submission = useSubmission(postNameAction);

return (
<form action={postNameAction} method="post">
<input type="text" name="name" />
<button type="submit">
{submission.pending ? "Adding..." : "Add"}
</button>
</form>
)
```tsx
import { Show } from "solid-js";
import { action, useSubmission } from "@solidjs/router";

const addTodoAction = action(async (formData: FormData) => {
const name = formData.get("name")?.toString() ?? "";
if (name.length <= 2) {
throw new Error("Name must be larger than 2 characters");
}
}, "addTodo");

function AddTodoForm() {
const submission = useSubmission(addTodoAction);
return (
<form action={addTodoAction} method="post">
<input name="name" />
<button type="submit">{submission.pending ? "Adding..." : "Add"}</button>
<Show when={submission.error}>
{(error) => (
<div>
<p>{error().message}</p>
<button onClick={() => submission.clear()}>Clear</button>
<button onClick={() => submission.retry()}>Retry</button>
</div>
)}
</Show>
</form>
);
}
```

:::info
Learn more about actions in the [`action`](/solid-router/reference/data-apis/action) docs.
:::info[Note]
If an action is executed multiple times, the last submission will be returned.
To access all submissions, `useSubmissions`[/solid-router/reference/data-apis/use-submissions] can be used.
:::

## Filtering Submissions

As an optional second parameter, the `useSubmission` helper can receive a filter function to only return the submission that matches the condition.
The filter receives the submitted dated as a parameter and should return a boolean value.
E.g.: action below will only submit if the name is "solid".

```tsx title="component.tsx" {4-8}
import { useSubmission } from "@solidjs/router";

function Component() {
const submission = useSubmission(postNameAction, ([formData]) => {
const name = formData.get("name") ?? "";

return name === "solid";
});

return (
<form action={postNameAction} method="post">
<input type="text" name="name" />
<button type="submit">
{submission.pending ? "Adding..." : "Add"}
</button>
</form>
)
}
```

## Optimistic Updates

When the form is submitted, the `submission` object will be updated with the new value and the `pending` property will be set to `true`.
This allows you to provide feedback to the user that the action is in progress.
Once the action is complete, the `pending` property will be set to `false` and the `result` property will be updated with final value.

```tsx tab title="TypeScript" {6,10-12}
// component.tsx
import { Show } from "solid-js";
import { useSubmission } from "@solidjs/router";

function Component() {
const submission = useSubmission(postNameAction);

return (
<>
<Show when={submission.input?.[0].get("name")}>
{(name) => <div>Optimistic: {name() as string}</div>}
</Show>
## Filter function

<Show when={submission.result?.name}>
{(name) => <div>Result: {name()}</div>}
</Show>
Optionally, `useSubmission` accepts a second parameter, which is a filter function.
This function is executed for each submission and returns the first submission that passes through the filter.
The filter function takes the submitted data as its parameter and should return `true` to select the submission and `false` otherwise.

<form method="post" action={sendData}>
<input type="text" name="name" required />
<button type="submit" disabled={submission.pending}>
{submission.pending ? "Submitting" : "Submit"}
</button>
</form>
</>
)
}
```

```tsx tab title="JavaScript" {6,10-12}
// component.jsx
import { Show } from "solid-js";
```tsx
import { useSubmission } from "@solidjs/router";

function Component() {
const submission = useSubmission(postNameAction);

return (
<>
<Show when={submission.input?.[0].get("name")}>
{(name) => <div>Optimistic: {name()}</div>}
</Show>

<Show when={submission.result?.name}>
{(name) => <div>Result: {name()}</div>}
</Show>

<form method="post" action={sendData}>
<input type="text" name="name" required />
<button type="submit" disabled={submission.pending}>
{submission.pending ? "Submitting" : "Submit"}
</button>
</form>
</>
)
import { addTodoAction } from "./actions";

function LatestTodo() {
const latestValidSubmission = useSubmission(
addTodoAction,
([formData]: [FormData]) => {
const name = formData.get("name")?.toString() ?? "";
return name.length > 2;
}
);
return <p>Latest valid submittion: {latestValidSubmission.result}</p>;
}
```

## Error Handling

If the action fails, the `submission` object will be updated with the error and the `pending` property will be set to `false`.
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.

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.
## Parameters

```tsx title="component.tsx" {12-18}
import { Show } from "solid-js";
import { useSubmission } from "@solidjs/router";
- **action**: The action for which you want to return submissions.
- **filter** (Optional): The filter function that receives the submitted data as its parameter.
It should return `true` if the submission passes the filter and `false` otherwise.

function Component() {
const submission = useSubmission(postNameAction);
## Returns

return (
<>
<Show when={submission.error}>
{(error) => (
<div>
<p>Error: {error.message}</p>
<button onClick={() => submission.clear()}>
Clear
</button>
<button onClick={async () => submission.retry()}>
Retry
</button>
</div>
)}
</Show>
`useSubmission` returns an object containing the following properties:

<form method="post" action={sendData}>
<input type="text" name="name" required />
<button type="submit" disabled={submission.pending}>
{submission.pending ? "Submitting" : "Submit"}
</button>
</form>
</>
)
}
```
- **input**: The input data of the action.
- **result**: The returned value of the action.
- **error**: Any error thrown from the action.
- **pending**: A boolean indicating whether the action is currently being executed.
- **clear**: A function to clear the results of the submission.
- **retry**: A function to re-execute the action.