|
| 1 | +--- |
| 2 | +title: action |
| 3 | +--- |
| 4 | + |
| 5 | +Actions are data mutations that can trigger invalidations and further routing. A list of prebuilt response helpers can be found below. |
| 6 | + |
| 7 | +```jsx |
| 8 | +import { action, revalidate, redirect } from "@solidjs/router" |
| 9 | + |
| 10 | +// anywhere |
| 11 | +const myAction = action(async (data) => { |
| 12 | + await doMutation(data); |
| 13 | + throw redirect("/", { revalidate: getUser.keyFor(data.id) }); // throw a response to do a redirect |
| 14 | +}); |
| 15 | + |
| 16 | +// in component |
| 17 | +<form action={myAction} method="post" /> |
| 18 | + |
| 19 | +//or |
| 20 | +<button type="submit" formaction={myAction}></button> |
| 21 | +``` |
| 22 | + |
| 23 | +Actions only work with post requests, so make sure to put `method="post"` on your form. |
| 24 | + |
| 25 | +Sometimes it might be easier to deal with typed data instead of `FormData` and adding additional hidden fields. For that reason Actions have a with method. That works similar to `bind` which applies the arguments in order. |
| 26 | + |
| 27 | +Picture an action that deletes Todo Item: |
| 28 | + |
| 29 | +```js |
| 30 | +const deleteTodo = action(async (formData: FormData) => { |
| 31 | + const id = Number(formData.get("id")) |
| 32 | + await api.deleteTodo(id) |
| 33 | +}) |
| 34 | + |
| 35 | +<form action={deleteTodo} method="post"> |
| 36 | + <input type="hidden" name="id" value={todo.id} /> |
| 37 | + <button type="submit">Delete</button> |
| 38 | +</form> |
| 39 | +``` |
| 40 | + |
| 41 | +Instead with `with` you can write this: |
| 42 | + |
| 43 | +```js |
| 44 | +const deleteUser = action(api.deleteTodo) |
| 45 | + |
| 46 | +<form action={deleteTodo.with(todo.id)} method="post"> |
| 47 | + <button type="submit">Delete</button> |
| 48 | +</form> |
| 49 | +``` |
| 50 | + |
| 51 | +### Notes of `<form>` implementation and SSR |
| 52 | + |
| 53 | +This requires stable references as you can only serialize a string as an attribute, and across SSR they'd need to match. The solution is providing a unique name. |
| 54 | + |
| 55 | +```jsx |
| 56 | +const myAction = action(async (args) => {}, "my-action"); |
| 57 | +``` |
| 58 | + |
| 59 | +## `useAction` |
| 60 | + |
| 61 | +Instead of forms you can use actions directly by wrapping them in a `useAction` primitive. This is how we get the router context. |
| 62 | + |
| 63 | +```jsx |
| 64 | +// in component |
| 65 | +const submit = useAction(myAction); |
| 66 | +submit(...args); |
| 67 | +``` |
| 68 | + |
| 69 | +The outside of a form context you can use custom data instead of formData, and these helpers preserve types. However, even when used with server functions (in projects like SolidStart) this requires client side javascript and is not Progressive Enhancible like forms are. |
| 70 | + |
| 71 | +## `useSubmission`/`useSubmissions` |
| 72 | + |
| 73 | +Are used to injecting the optimistic updates while actions are in flight. They either return a single Submission(latest) or all that match with an optional filter function. |
| 74 | + |
| 75 | +```jsx |
| 76 | +type Submission<T, U> = { |
| 77 | + input: T; |
| 78 | + result: U; |
| 79 | + error: any; |
| 80 | + pending: boolean |
| 81 | + clear: () => {} |
| 82 | + retry: () => {} |
| 83 | +} |
| 84 | + |
| 85 | +const submissions = useSubmissions(action, (input) => filter(input)); |
| 86 | +const submission = useSubmission(action, (input) => filter(input)); |
| 87 | +``` |
0 commit comments