From f2293e5ed2222fe43339e61966ae190c4157f781 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Fri, 16 May 2025 11:46:09 +0330 Subject: [PATCH 01/32] Fix useSubmission --- .../reference/data-apis/use-submission.mdx | 216 +++++------------- 1 file changed, 61 insertions(+), 155 deletions(-) diff --git a/src/routes/solid-router/reference/data-apis/use-submission.mdx b/src/routes/solid-router/reference/data-apis/use-submission.mdx index 1ba58fa79..b4e9f969d 100644 --- a/src/routes/solid-router/reference/data-apis/use-submission.mdx +++ b/src/routes/solid-router/reference/data-apis/use-submission.mdx @@ -1,175 +1,81 @@ --- title: useSubmission -use_cases: >- - form feedback, pending states, optimistic updates, error handling, single - submission tracking -tags: - - forms - - submissions - - pending - - optimistic - - errors - - feedback -version: '1.0' -description: >- - Track form submission states with useSubmission. Handle pending feedback, - optimistic updates, and errors for single forms. --- -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 ( -
- - -
- ) +```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 ( +
+ + + + {(error) => ( +
+

{error().message}

+ + +
+ )} +
+
+ ); } ``` -:::note -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 data 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 ( -
- - -
- ) -} -``` - -## 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 ( - <> - - {(name) =>
Optimistic: {name() as string}
} -
+## Filter function - - {(name) =>
Result: {name()}
} -
+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. -
- - -
- - ) -} -``` - -```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 ( - <> - - {(name) =>
Optimistic: {name()}
} -
- - - {(name) =>
Result: {name()}
} -
- -
- - -
- - ) +import { addTodoAction } from "./actions"; + +function LatestTodo() { + const latestValidSubmission = useSubmission( + addTodoAction, + ([formData]: [FormData]) => { + const name = formData.get("name")?.toString() ?? ""; + return name.length > 2; + } + ); + return

Latest valid submittion: {latestValidSubmission.result}

; } ``` -## 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 ( - <> - - {(error) => ( -
-

Error: {error.message}

- - -
- )} -
+`useSubmission` returns an object containing the following properties: -
- - -
- - ) -} -``` +- **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. From bd691c224f3f3b8f2f7bc16cc0559dbea4ce9188 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Sun, 8 Jun 2025 11:42:11 +0330 Subject: [PATCH 02/32] Update --- .../reference/data-apis/use-submission.mdx | 31 +- .../reference/data-apis/use-submissions.mdx | 264 ++++++------------ 2 files changed, 110 insertions(+), 185 deletions(-) diff --git a/src/routes/solid-router/reference/data-apis/use-submission.mdx b/src/routes/solid-router/reference/data-apis/use-submission.mdx index b4e9f969d..962efa534 100644 --- a/src/routes/solid-router/reference/data-apis/use-submission.mdx +++ b/src/routes/solid-router/reference/data-apis/use-submission.mdx @@ -2,8 +2,7 @@ title: useSubmission --- -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. +The `useSubmission` function retrieves the state of the most recent submission for a given action. ```tsx import { Show } from "solid-js"; @@ -37,15 +36,14 @@ function AddTodoForm() { ``` :::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. +To access the state of all submissions, `useSubmissions`[/solid-router/reference/data-apis/use-submissions] can be used. ::: ## Filter function -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. +The `useSubmission` function optionally accepts a second parameter, which is a filter function. +This function is executed for each submission in the order they were created, and the first submission that meet the filter criteria is returned by `useSubmission`. +The filter function recieves the input data of the action as its parameter and must return `true` to select the submission or `false` otherwise. ```tsx import { useSubmission } from "@solidjs/router"; @@ -59,23 +57,28 @@ function LatestTodo() { return name.length > 2; } ); - return

Latest valid submittion: {latestValidSubmission.result}

; + return

Latest valid submission: {latestValidSubmission.result}

; } ``` ## Parameters -- **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. +- **action**: The action for which you want to get the most recent submission. +- **filter** (optional): A filter function. + When provided, it executes on each submission in the order of creation, returning the first submission that passes the filter. + It receives the input data of the action as its parameter and must return `true` for the submission to be selected and `false` otherwise. ## Returns `useSubmission` returns an object containing the following properties: - **input**: The input data of the action. -- **result**: The returned value of the action. + This is a reactive value. +- **result**: The value returned from the action. + This is a reactive value. - **error**: Any error thrown from the action. + This is a reactive value. - **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. + This is a reactive value. +- **clear**: A function that clears the result of the submission. +- **retry**: A function that re-executes the submission with the same input. diff --git a/src/routes/solid-router/reference/data-apis/use-submissions.mdx b/src/routes/solid-router/reference/data-apis/use-submissions.mdx index f03cb70b4..51e09dbaa 100644 --- a/src/routes/solid-router/reference/data-apis/use-submissions.mdx +++ b/src/routes/solid-router/reference/data-apis/use-submissions.mdx @@ -16,189 +16,111 @@ description: >- history, handle errors, and show optimistic updates. --- -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 an iterable of all submitted actions while its component is mounted. With an optional second parameter for a filter function. - -:::tip -If you only care for the latest submission, you can use the [`useSubmission`](/solid-router/reference/data-apis/use-submission) helper. -::: - -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. - -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. - -```tsx title="component.tsx" {4,9-20, 23} -import { useSubmissions } from "@solidjs/router"; - -function Component() { - const submissions = useSubmissions(postNameAction); - - return ( -
- - - -
- ) +The `useSubmissions` function retrieves the state of all submissions for a given action. + +```tsx +import { For, Show } from "solid-js"; +import { action, useSubmissions } 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"); + } + return name; +}, "addTodo"); + +export default function AddTodoForm() { + const submissions = useSubmissions(addTodoAction); + return ( +
+
+ + +
+ + {(submission) => ( +
+ Adding "{submission.input[0].get("name")?.toString()}" + + (pending...) + + + (completed) + + + {(error) => ( + <> + {` (Error: ${error().message})`} + + + )} + +
+ )} +
+
+ ); } ``` -:::note -To trigger a submission, [actions](https://docs.solidjs.com/) can be used. +:::info[Note] +To access the state of the most recent submission, `useSubmission`[/solid-router/reference/data-apis/use-submission] can be used. ::: -## Filtering Submissions +## Filter function -As an optional second parameter, the `useSubmissions` 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". +The `useSubmissions` function optionally accepts a second parameter, which is a filter function. +This function is executed for each submission in the order they were created, and only the submissions that meet the filter criteria are returned by `useSubmissions`. +The filter function recieves the input data of the action as its parameter and must return `true` to select the submission or `false` otherwise. -```tsx title="component.tsx" {4-8} +```tsx import { useSubmissions } from "@solidjs/router"; - -function Component() { - const submissions = useSubmissions(postNameAction, ([formData]) => { - const name = formData.get("name") ?? ""; - - return name === "solid"; - }); - - return ( -
- - - -
- ) +import { addTodoAction } from "./actions"; + +function FailedTodos() { + const failedSubmissions = useSubmissions( + addTodoAction, + ([formData]: [FormData]) => { + const name = formData.get("name")?.toString() ?? ""; + return name.length <= 2; + } + ); + return ( +
+

Failed submissions:

+ + {(submission) => ( +
+ {submission.input[0].get("name")?.toString()} + +
+ )} +
+
+ ); } ``` -## 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. +## Parameters -```tsx tab title="TypeScript" {6,13-20} -// component.tsx -import { Show } from "solid-js"; -import { useSubmissions } from "@solidjs/router"; - -function Component() { - const submissions = useSubmissions(postNameAction); +- **action**: The action for which you want to get the submissions. +- **filter** (optional): A filter function. + When provided, it executes on each submission in the order of creation, returning the submissions that pass the filter. + It receives the input data of the action as its parameter and must return `true` to select the submission and `false` otherwise. - return ( -
- - - -
- ) -} -``` +`useSubmissions` returns an array of submissions. +Each submission is an object containing the following properties: -```tsx tab title="JavaScript" {6,13-20} -// component.jsx -import { Show } from "solid-js"; -import { useSubmissions } from "@solidjs/router"; - -function Component() { - const submissions = useSubmissions(postNameAction); - - return ( -
- - - -
- ) -} -``` - -## 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. - -```tsx title="component.tsx" {12-18} -import { Show } from "solid-js"; -import { useSubmissions } from "@solidjs/router"; - -function Component() { - const submissions = useSubmissions(postNameAction); - - return ( -
- - - -
- ) -} -``` +- **input**: The input data of the action. + This is a reactive value. +- **result**: The value returned from the action. + This is a reactive value. +- **error**: Any error thrown from the action. + This is a reactive value. +- **pending**: A boolean indicating whether the action is currently being executed. + This is a reactive value. +- **clear**: A function that clears the result of the submission. +- **retry**: A function that re-executes the submission with the same input. From 1f94c982171ac4fa6ebbe0f716c0b1106fd6e867 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Wed, 11 Jun 2025 18:58:40 +0330 Subject: [PATCH 03/32] Update --- .../reference/data-apis/action.mdx | 147 ++++++++---------- 1 file changed, 62 insertions(+), 85 deletions(-) diff --git a/src/routes/solid-router/reference/data-apis/action.mdx b/src/routes/solid-router/reference/data-apis/action.mdx index f32f1ca7b..65bc5688c 100644 --- a/src/routes/solid-router/reference/data-apis/action.mdx +++ b/src/routes/solid-router/reference/data-apis/action.mdx @@ -1,113 +1,90 @@ --- title: action -use_cases: >- - forms, user input, data mutations, optimistic updates, form submissions, - server actions, post requests -tags: - - actions - - forms - - mutations - - post - - validation - - optimistic-updates - - server -version: '1.0' -description: >- - Learn how to handle form submissions and data mutations in SolidJS with - actions, including optimistic updates and server-side processing. --- -Actions are data mutations that can trigger invalidations and further routing. -A list of prebuilt response helpers can be found below. +The `action` function wraps an asynchronous function and returns an action. +Actions enable data mutations and side effects, often in response to user interactions such as form submissions. +To learn more about actions, see [the actions documentation](/solid-router/concepts/actions). -```jsx -import { action, revalidate, redirect } from "@solidjs/router" +```tsx +import { action } from "@solidjs/router"; -// anywhere -const myAction = action(async (data) => { - await doMutation(data); - throw redirect("/", { revalidate: getUser.keyFor(data.id) }); // throw a response to do a redirect +const addTodoAction = action(async (name: string) => { + await fetch("https://api.com/todos", { + method: "POST", + body: JSON.stringify({ name }), + }); }); - -// in component -
- -//or - - ``` -Actions only work with **post** requests. -This means forms require `method="post"`. +## Forms -A `with` method can be used when typed data is required. -This removes the need to use `FormData` or other additional hidden fields. -The `with` method works similar to [`bind`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind), which applies the arguments in order. +Actions can be used to handle form submissions by passing them to the `action` prop in the [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/form) element. +The form values will be accessible through the first parameter of the action function, which is a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData) object. -Without `with`: +Please note that the `` element **must** have `method="post"`. +When using Server-Side Rendering (SSR), you must provide a unique name as the second parameter to the action function. -```jsx -const deleteTodo = action(async (formData: FormData) => { - const id = Number(formData.get("id")) - await api.deleteTodo(id) -}) - - - - -
+```tsx +import { action } from "@solidjs/router"; -``` - -Using `with`: - -```jsx del={5,6} ins={7} -const deleteTodo = action(async (id: number) => { - await api.deleteTodo(id) -}) - -
- - - -
+const addTodoAction = action(async (formData: FormData) => { + const name = formData.get("name")?.toString(); + await fetch("https://api.com/todos", { + method: "POST", + body: JSON.stringify({ name }), + }); +}, "add-todo"); +function TodoForm() { + return ( +
+ + +
+ ); +} ``` -:::tip -In [SolidStart](/solid-start) apps, it's recommended to use the [`"use server"`](/solid-start/reference/server/use-server) directive to leverage server-side caching. -::: - -## Notes of `
` implementation and SSR - -This requires stable references because a string can only be serialized as an attribute, and it is crucial for consistency across SSR. where these references must align. -The solution is to provide a unique name. +### The `with` method -```jsx -const myAction = action(async (args) => {}, "my-action"); -``` - -## `useAction` +Actions have a `with` method that is used to pass additional arguments to the action. +The parameters passed to the `with` method are forwarded to the action function in the same order. +The `FormData` is still available as the last parameter. -Instead of forms, actions can directly be wrapped in a `useAction` primitive. -This is how router context is created. +```tsx +import { action } from "@solidjs/router"; -```jsx -// in component -const submit = useAction(myAction); -submit(...args); +const addTodoAction = action(async (userId: number, formData: FormData) => { + const name = formData.get("name")?.toString(); + await fetch("https://api.com/todos", { + method: "POST", + body: JSON.stringify({ userId, name }), + }); +}); +function TodoForm() { + const userId = 1; + return ( + + + +
+ ); +} ``` -The outside of a form context can use custom data instead of `formData`. -These helpers preserve types. +## Response helpers -However, even when used with server functions, such as with [SolidStart](https://start.solidjs.com/getting-started/what-is-solidstart), this requires client-side JavaScript and is not progressively enhanceable like forms are. +Solid Router provides three response helpers that customize the behavior of the action: -## `useSubmission`/`useSubmissions` +- [`json`](/solid-router/reference/response-helpers/json): Allows returning JSON from the action, which will be accessible as the return value when invoking the action using [`useAction`](/solid-router/reference/data-apis/use-action). +- [`redirect`](/solid-router/reference/response-helpers/redirect): Performs a redirect. +- [`reload`](/solid-router/reference/response-helpers/reload): Revalidates queries. -These functions are used when incorporating optimistic updates during ongoing actions. -They provide either a singular Submission (the latest one), or a collection of Submissions that match, with an optional filtering function. +Response helpers return a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) object with custom properties recognized by Solid Router. +When a `Response` object is either returned or thrown from an action function, Solid Router handles it automatically. +It's advised not to construct a `Response` object without using response helpers. ```jsx type Submission = { From b34f043e55d7e5578048f2e0a8061dd8a8a1f028 Mon Sep 17 00:00:00 2001 From: Amir Hossein Hashemi <87268103+amirhhashemi@users.noreply.github.com> Date: Thu, 19 Jun 2025 01:20:19 +0330 Subject: [PATCH 04/32] Update info callout titles (#1207) --- .../solid-router/reference/data-apis/use-submission.mdx | 2 +- src/ui/callout.tsx | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/routes/solid-router/reference/data-apis/use-submission.mdx b/src/routes/solid-router/reference/data-apis/use-submission.mdx index 962efa534..4c2055bcb 100644 --- a/src/routes/solid-router/reference/data-apis/use-submission.mdx +++ b/src/routes/solid-router/reference/data-apis/use-submission.mdx @@ -35,7 +35,7 @@ function AddTodoForm() { } ``` -:::info[Note] +:::note To access the state of all submissions, `useSubmissions`[/solid-router/reference/data-apis/use-submissions] can be used. ::: diff --git a/src/ui/callout.tsx b/src/ui/callout.tsx index 9878c4266..a69862a88 100644 --- a/src/ui/callout.tsx +++ b/src/ui/callout.tsx @@ -101,7 +101,11 @@ export function Callout(props: CalloutProps) { +======= + +>>>>>>> 0c3b40b1 (Update info callout titles (#1207)) {props.type || "Note"} } From 32333b39ca4781e50a3039c6cd807acd3c376424 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Wed, 2 Jul 2025 07:38:29 +0330 Subject: [PATCH 05/32] update --- src/routes/solid-router/concepts/actions.mdx | 231 ++++++++++-------- .../reference/data-apis/use-submission.mdx | 2 +- .../reference/data-apis/use-submissions.mdx | 4 +- 3 files changed, 126 insertions(+), 111 deletions(-) diff --git a/src/routes/solid-router/concepts/actions.mdx b/src/routes/solid-router/concepts/actions.mdx index 25c09e7bc..211f6ddce 100644 --- a/src/routes/solid-router/concepts/actions.mdx +++ b/src/routes/solid-router/concepts/actions.mdx @@ -17,151 +17,166 @@ description: >- isomorphic data flows with progressive enhancement support. --- -When developing applications, it is common to need to communicate new information to the server based on user interactions. -Actions are Solid Router’s solution to this problem. +Actions provide a powerful and flexible mechanism for handling data mutations and side effects. +They are designed to simplify your application's data flow, ensure a consistent user experience, and integrate seamlessly with Solid's reactivity. -## What are actions? +Actions provide several key benefits: -Actions are asynchronous processing functions that allow you to submit data to your server and receive a response. -They are isomorphic, meaning they can run either on the server or the client, depending on what is needed. -This flexibility makes actions a powerful tool for managing and tracking data submissions. - -### How actions work - -Actions represent the server-side part of an [HTML form](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form). -They handle submissions through POST requests, allowing you to easily use HTML forms to send data. - -When a user performs an action, such as submitting a form, the data is sent to the server for processing via an action. - -### Benefits of using actions - -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. -2. **Asynchronous processing**: Actions handle data submissions asynchronously, ensuring that your application remains responsive. -3. **Simplified data handling**: By using actions, the process of managing and tracking data submissions can be streamlined, reducing the complexity of your application. +- **Centralized Logic**: Encapsulate the logic for data modifications in a single, reusable function. +- **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. +- **Automatic Data Revalidation**: By default, after an action successfully completes, Solid Router revalidates any queries on the same page. + This ensures your UI reflects the latest data without manual intervention. +- **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. ## Creating actions -To create an action, use the `action` function from the `@solidjs/router` package. -This function takes an asynchronous function as an argument and returns a new function that can be used to submit data. +At their core, actions are **asynchronous functions** that you define using the `action` function. +The [`action`](/solid-router/reference/data-apis/action) function takes your asynchronous function and returns an action object. + +To define an action, import the `action` function from `@solidjs/router` and pass it your asynchronous logic: ```tsx import { action } from "@solidjs/router"; -const echo = action(async (message: string) => { - // Simulates an asynchronous operation, such as an API call - await new Promise((resolve, reject) => setTimeout(resolve, 1000)); - console.log(message); +const addPostAction = action(async (title: string) => { + const response = await fetch("https://api.com/posts", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ title }), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to add post"); + } + + return await response.json(); }); ``` -In this example, the `echo` action simulates a fetch call with a 1 second delay before logging the message to the console. -The `echo` action will act as a backend, however, it can be substituted for any API provided it can be run on the client. -Typically, route actions are used with some sort of solution like fetch or GraphQL. +In this example, `addPostAction` handles sending a POST request to create a new post. +The return value of the action can be accessed later when tracking action state. -:::tip -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. +:::note[Server-Side Rendering (SSR)] +When using actions with SSR, you must provide a unique name string as the second parameter to the `action` function. +This is crucial for Solid Router to correctly identify and re-run actions on the server. +We'll explore this in more detail in the Handling Form Submissions section. ::: -### Using actions - -To use the action, you can call it from within a component using [`useAction`](/solid-router/reference/data-apis/use-action). -This returns a function that can be called with the necessary arguments to trigger the action. - -```tsx del={1} ins={2,9-13} -import { action } from "@solidjs/router"; -import { action, useAction } from "@solidjs/router"; - -const echo = action(async (message: string) => { - await new Promise((resolve, reject) => setTimeout(resolve, 1000)); - console.log(message); -}); - -export function MyComponent() { - const myEcho = useAction(echo); - - myEcho("Hello from Solid!"); -} -``` - -In this component, `useAction` is used to get a reference to the `echo` action. -The action is then called with the message `"Hello from Solid!"`, which will be logged to the console after a 1 second delay. - -### Returning data from actions +## How to Use Actions -In many cases, after submitting data, the server sends some data back as well. -This may be in the form of an error message if something has failed or the results of a successful operation. -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. +Solid Router offers two primary ways to invoke an action: -To access the action's result, you must pass the action to `useSubmission`: +1. **Via the `
` element's `action` prop**: This is the recommended approach for most data mutations, especially those triggered by user input, as it provides **progressive enhancement**. +2. **Programmatically with `useAction`**: For scenarios where you need to trigger an action outside of a form context. -```tsx del={1} ins={2,11,15-17} -import { action, useAction } from "@solidjs/router"; -import { action, useAction, useSubmission } from "@solidjs/router"; +### Handling Form Submissions with the `action` prop -const echo = action(async (message: string) => { - await new Promise((resolve, reject) => setTimeout(resolve, 1000)); - return message; -}); +Solid Router extends the standard HTML `` element to accept an `action` prop, allowing you to handle your form submissions to an action. +This method provides the best user experience due to progressive enhancement. -export function MyComponent() { - const myEcho = useAction(echo); - const echoing = useSubmission(echo); +When using actions with ``: - myEcho("Hello from solid!"); +1. The `` element **must** have `method="post"`. +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. +3. For SSR environments, you **must** provide a unique name string as the second parameter to the `action` function. + This name is used by Solid Router to uniquely identify and serialize the action across the client and server. - setTimeout(() => myEcho("This is a second submission!"), 1500); +```tsx +import { action } from "@solidjs/router"; - return

{echoing.result}

; +const addPostAction = action(async (formData: FormData) => { + const title = formData.get("title")?.toString(); + + if (!title || title.trim() === "") { + throw new Error("Post title cannot be empty."); + } + + const response = await fetch("https://api.com/posts", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ title }), + }); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to add post"); + } +}, "add-post"); + +function AddPostForm() { + return ( + + + + +
+ ); } ``` -Using `useSubmission` leaves the implementation details of how you trigger `echo` up to you. -When handling user inputs, for example, it is better to use a `form` for a multitude of reasons. +When this form is submitted, `addPostFormAction` will be invoked with the `FormData` containing the form values. -## Using forms to submit data +:::tip[File Uploads] -When submitting data with actions, it is recommended to use HTML forms. -These forms can be used prior to JavaScript loading, which creates instantly interactive applications. -This also inherently provides accessibility benefits, saving the time of designing a custom UI library that may not have these benefits. +If your form includes file inputs, ensure your
element has enctype="multipart/form-data" to correctly send the file data. -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). -To use actions with forms, pass the action to the `action` property of your form. -This creates progressively enhanced forms that work even when JavaScript is disabled. +```tsx + + + +
+``` -If you do not return a `Response` from your action, the user will stay on the same page and responses will be re-triggered. -Using a `redirect` can tell the browser to navigate to a new page. +::: +#### Passing additional data -```tsx -import { action, redirect } from "@solidjs/router"; +Sometimes, your action might need additional data that isn't part of the form's inputs. +You can pass these additional arguments using the `with` method on your action. -const isAdmin = action(async (formData: FormData) => { - await new Promise((resolve, reject) => setTimeout(resolve, 1000)); +Arguments passed to `with` will be forwarded to your action function before the `FormData` object. - const username = formData.get("username"); +```tsx +import { action } from "@solidjs/router"; - if (username === "admin") throw redirect("/admin"); - return new Error("Invalid username"); +const updatePostAction = action(async (postId: string, formData: FormData) => { + const newTitle = formData.get("title")?.toString(); + + if (!newTitle || newTitle.trim() === "") { + throw new Error("Post title cannot be empty."); + } + + const response = await fetch( + `https://api.com/posts/${encodeURIComponent(newTitle)}`, + { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ title: newTitle }), + } + ); + + if (!response.ok) { + const errorData = await response.json(); + throw new Error(errorData.message || "Failed to update post"); + } }); -export function MyComponent() { - - return ( -
- - - -
- ); +function PostEditForm(props: { postId: string }) { + return ( +
+ + + +
+ ); } ``` -**Note:** If you are uploading files make sure you include `enctype="multipart/form-data"` to your `
` element. - -## Error handling - -Rather than throwing errors, it is recommended to return them from actions. -This helps with the typing of submissions that would be used with `useSubmission`. -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. - -Additionally, when using server actions, it is good practice to handle errors on the server to sanitize error messages. +Here, `updatePostAction` receives `postId` (passed via `with`), and then the `formData` from the form. diff --git a/src/routes/solid-router/reference/data-apis/use-submission.mdx b/src/routes/solid-router/reference/data-apis/use-submission.mdx index 4c2055bcb..f23ade50e 100644 --- a/src/routes/solid-router/reference/data-apis/use-submission.mdx +++ b/src/routes/solid-router/reference/data-apis/use-submission.mdx @@ -13,7 +13,7 @@ const addTodoAction = action(async (formData: FormData) => { if (name.length <= 2) { throw new Error("Name must be larger than 2 characters"); } -}, "addTodo"); +}, "add-todo"); function AddTodoForm() { const submission = useSubmission(addTodoAction); diff --git a/src/routes/solid-router/reference/data-apis/use-submissions.mdx b/src/routes/solid-router/reference/data-apis/use-submissions.mdx index 51e09dbaa..8777bdb73 100644 --- a/src/routes/solid-router/reference/data-apis/use-submissions.mdx +++ b/src/routes/solid-router/reference/data-apis/use-submissions.mdx @@ -28,7 +28,7 @@ const addTodoAction = action(async (formData: FormData) => { throw new Error("Name must be larger than 2 characters"); } return name; -}, "addTodo"); +}, "add-todo"); export default function AddTodoForm() { const submissions = useSubmissions(addTodoAction); @@ -64,7 +64,7 @@ export default function AddTodoForm() { } ``` -:::info[Note] +:::note To access the state of the most recent submission, `useSubmission`[/solid-router/reference/data-apis/use-submission] can be used. ::: From 74f99ef1279a5837424f05e969837f60b146cfde Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Thu, 3 Jul 2025 18:03:54 +0330 Subject: [PATCH 06/32] update --- src/routes/solid-router/concepts/actions.mdx | 200 ++++++++++++++++++- src/ui/callout.tsx | 4 - 2 files changed, 190 insertions(+), 14 deletions(-) diff --git a/src/routes/solid-router/concepts/actions.mdx b/src/routes/solid-router/concepts/actions.mdx index 211f6ddce..c7a826d2f 100644 --- a/src/routes/solid-router/concepts/actions.mdx +++ b/src/routes/solid-router/concepts/actions.mdx @@ -22,7 +22,6 @@ They are designed to simplify your application's data flow, ensure a consistent Actions provide several key benefits: -- **Centralized Logic**: Encapsulate the logic for data modifications in a single, reusable function. - **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. - **Automatic Data Revalidation**: By default, after an action successfully completes, Solid Router revalidates any queries on the same page. This ensures your UI reflects the latest data without manual intervention. @@ -49,10 +48,10 @@ const addPostAction = action(async (title: string) => { if (!response.ok) { const errorData = await response.json(); - throw new Error(errorData.message || "Failed to add post"); + return { success: false, message: errorData.message }; } - return await response.json(); + return { success: true, data: await response.json() }; }); ``` @@ -65,7 +64,7 @@ This is crucial for Solid Router to correctly identify and re-run actions on the We'll explore this in more detail in the Handling Form Submissions section. ::: -## How to Use Actions +## Using actions Solid Router offers two primary ways to invoke an action: @@ -91,7 +90,7 @@ const addPostAction = action(async (formData: FormData) => { const title = formData.get("title")?.toString(); if (!title || title.trim() === "") { - throw new Error("Post title cannot be empty."); + return { success: false, message: "Post title cannot be empty." }; } const response = await fetch("https://api.com/posts", { @@ -104,9 +103,11 @@ const addPostAction = action(async (formData: FormData) => { if (!response.ok) { const errorData = await response.json(); - throw new Error(errorData.message || "Failed to add post"); + return { success: false, message: errorData.message }; } -}, "add-post"); + + return { success: true }; +}, "addPost"); function AddPostForm() { return ( @@ -123,7 +124,7 @@ When this form is submitted, `addPostFormAction` will be invoked with the `FormD :::tip[File Uploads] -If your form includes file inputs, ensure your element has enctype="multipart/form-data" to correctly send the file data. +If your form includes file inputs, ensure your `` element has `enctype="multipart/form-data"` to correctly send the file data. ```tsx @@ -148,7 +149,7 @@ const updatePostAction = action(async (postId: string, formData: FormData) => { const newTitle = formData.get("title")?.toString(); if (!newTitle || newTitle.trim() === "") { - throw new Error("Post title cannot be empty."); + return { success: false, message: "Post title cannot be empty." }; } const response = await fetch( @@ -164,8 +165,10 @@ const updatePostAction = action(async (postId: string, formData: FormData) => { if (!response.ok) { const errorData = await response.json(); - throw new Error(errorData.message || "Failed to update post"); + return { success: false, message: errorData.message }; } + + return { success: true }; }); function PostEditForm(props: { postId: string }) { @@ -180,3 +183,180 @@ function PostEditForm(props: { postId: string }) { ``` Here, `updatePostAction` receives `postId` (passed via `with`), and then the `formData` from the form. + +### Invoking Actions Programmatically with `useAction` + +While forms are highly recommended for progressive enhancement, there are scenarios where you might need to trigger an action directly from a Solid component, outside of a `` element. +The `useAction` primitive allows you to do this. + +`useAction` takes an action as an argument and returns a function that, when called, will invoke the action with the provided parameters. + +```tsx +import { action, useAction } from "@solidjs/router"; + +const likePostAction = action(async (postId: string) => { + await fetch(`https://api.com/posts/${encodeURIComponent(postId)}/likes`, { + method: "POST", + }); +}); + +function LikePostButton(props: { postId: string }) { + const likePost = useAction(); + + return ; +} +``` + +In this example, `likePost` is a function that can be called with arguments matching `likePostAction`. +When the button is clicked, `likePostAction` invokes for the specific post ID. + +## Query revalidation + +By default, when an action successfully finishes its execution, Solid Router will automatically revalidate all queries on the same page. +This means you typically don't have to manually refetch data after a mutation. +For example, if you add a new post using an action, any query on that page fetching a list of posts will automatically re-run and update your UI. + +```tsx +import { query, action, createAsync } from "@solidjs/router"; + +const getPostsQuery = query(async () => { + const response = await fetch("https://api.com/posts"); + return await response.json(); +}, "getPosts"); + +const addPostAction = action(async (formData: FormData) => { + const title = formData.get("title")?.toString(); + await fetch("https://api.com/posts", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title }), + }); +}, "addPost"); + +function PostsPage() { + // This query will automatically revalidate after addPostAction completes. + const posts = createAsync(() => getPostsQuery()); + + return ( +
+

All Posts

+ {(post) =>

{post.title}

}
+ +

Add New Post

+ + + + +
+ ); +} +``` + +While automatic revalidation is convenient, there are times you need more control. +Solid Router's response helpers allow you to precisely manage which queries are revalidated, or even disable revalidation entirely for a specific action. +You'll learn more about these helpers in the next section. + +## Response helpers + +Solid Router provides special response helpers that enable your actions to explicitly dictate routing and query revalidation: + +- [`json`](/solid-router/reference/response-helpers/json): Allows customizing query revalidation behavior when the action returns something. + While it's not necessary to use this helper to return a value from an action (a plain JavaScript value also works), the `json` helper can be used when you want to return data **and** customize query revalidation. +- [`redirect`](/solid-router/reference/response-helpers/redirect): Performs a redirect. +- [`reload`](/solid-router/reference/response-helpers/reload): Revalidates queries. + +You can either `return` or `throw` the result of a response helper in order for Solid Router to handle it. +Throwing might be preferable in TypeScript environemnts since it doesn't conflict with TypeScript's type system. + +```tsx +import { action, redirect, json, reload } from "@solidjs/router"; + +// Example 1: Redirecting after a successful login +const loginAction = action(async (formData: FormData) => { + // ...Login logic + + throw redirect("/dashboard"); +}); + +// Example 2: Returning data with specific revalidation control +const savePreferencesAction = action(async (formData: FormData) => { + // ...Save preferences logic + + return json( + { success: true, message: "Preferences saved!" }, + { revalidate: ["userPreferences"] } + ); +}); + +// Example 3: Disabling revalidation +const logActivityAction = action(async (activityData: FormData) => { + // ...Log activity to server + + // Don't revalidate any queries on the current page for this action + throw reload({ revalidate: [] }); +}); +``` + +## Tracking action state + +When an action is executing, your UI often needs to reflect its current state. +Solid Router provides the `useSubmission` and `useSubmissions` primitives to track the lifecycle of your actions. + +The `useSubmission` hook returns an object with reactive values that represent the state of the _most recent_ submission for a given action. +This is perfect for displaying pending states on buttons, showing the latest error, or displaying the outcome of the action. + +```tsx +import { Show } from "solid-js"; +import { action, useSubmission } from "@solidjs/router"; + +const saveSettingsAction = action(async (formData: FormData) => { + const email = formData.get("email")?.toString(); + + if (!email || !email.includes("@")) { + throw new Error("Please enter a valid email address."); + } + + await new Promise((res) => setTimeout(res, 2000)); // Simulate API call + + return { success: true, message: "Settings saved successfully!" }; +}, "save-settings"); + +function UserSettingsForm() { + const submission = useSubmission(saveSettingsAction); + + return ( +
+ + + + + + + {(error) => ( +
+

Error: {error().message}

+ + +
+ )} +
+ + {(result) => ( +

{result().message}

+ )} +
+
+ ); +} +``` + +In this example, the form's submit button is disabled during `submission.pending`, and appropriate messages are shown based on `submission.error` or `submission.result`. +The `clear` method resets the submission state, and `retry` re-executes the last submission with its original input. + +For more details, see the [`useSubmission` API reference](/solid-router/reference/data-apis/use-submission). + +:::tip +If you need to track multiple concurrent or recent submissions for an action (e.g., a list of file uploads, a queue of items being processed), the [`useSubmissions` primitive](/solid-router/reference/data-apis/use-submissions) can be used. +::: diff --git a/src/ui/callout.tsx b/src/ui/callout.tsx index a69862a88..9878c4266 100644 --- a/src/ui/callout.tsx +++ b/src/ui/callout.tsx @@ -101,11 +101,7 @@ export function Callout(props: CalloutProps) { -======= - ->>>>>>> 0c3b40b1 (Update info callout titles (#1207)) {props.type || "Note"} } From 629b1ed51165851efb4d53dd0eedc6ab7f2e7c08 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Tue, 5 Aug 2025 06:43:33 +0330 Subject: [PATCH 07/32] update --- src/routes/solid-router/concepts/actions.mdx | 482 ++++++++++++------- 1 file changed, 318 insertions(+), 164 deletions(-) diff --git a/src/routes/solid-router/concepts/actions.mdx b/src/routes/solid-router/concepts/actions.mdx index c7a826d2f..4dd0620a6 100644 --- a/src/routes/solid-router/concepts/actions.mdx +++ b/src/routes/solid-router/concepts/actions.mdx @@ -17,22 +17,24 @@ description: >- isomorphic data flows with progressive enhancement support. --- -Actions provide a powerful and flexible mechanism for handling data mutations and side effects. -They are designed to simplify your application's data flow, ensure a consistent user experience, and integrate seamlessly with Solid's reactivity. +Actions simplify handling data mutations and side effects in your applications. +They are ideal for handling form submissions, API calls, and other updates that modify your application's state on the server or client. -Actions provide several key benefits: +Actions provide several benefits: -- **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. -- **Automatic Data Revalidation**: By default, after an action successfully completes, Solid Router revalidates any queries on the same page. +- **Integrated state management:** + Solid Router automatically tracks the execution state of an action (pending, successful, error), simplifying reactive UI feedback. +- **Automatic data revalidation:** + After an action successfully completes, Solid Router revalidates relevant queries that are used on the same page. This ensures your UI reflects the latest data without manual intervention. -- **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. +- **Progressive enhancement:** + When used with HTML forms, actions enable functionality even if JavaScript is not yet loaded. -## Creating actions +## Defining actions -At their core, actions are **asynchronous functions** that you define using the `action` function. -The [`action`](/solid-router/reference/data-apis/action) function takes your asynchronous function and returns an action object. - -To define an action, import the `action` function from `@solidjs/router` and pass it your asynchronous logic: +At their core, actions are **asynchronous operations**. +You define them by wrapping your asynchronous logic with the [`action`](/solid-router/reference/data-apis/action) function. +When the action is invoked, Solid Router executes your defined logic. ```tsx import { action } from "@solidjs/router"; @@ -48,71 +50,48 @@ const addPostAction = action(async (title: string) => { if (!response.ok) { const errorData = await response.json(); - return { success: false, message: errorData.message }; + return { ok: false, message: errorData.message }; } - return { success: true, data: await response.json() }; + return { ok: true }; }); ``` -In this example, `addPostAction` handles sending a POST request to create a new post. -The return value of the action can be accessed later when tracking action state. +In this example, `addPostAction` sending a POST request to create a new post. +If the request failed, it returns an error. +We will cover error handling later in this page. -:::note[Server-Side Rendering (SSR)] -When using actions with SSR, you must provide a unique name string as the second parameter to the `action` function. -This is crucial for Solid Router to correctly identify and re-run actions on the server. -We'll explore this in more detail in the Handling Form Submissions section. -::: +## Invoking actions -## Using actions +You can invoke an action in two primary ways: through an HTML `
` or programmatically with the `useAction` primitive. -Solid Router offers two primary ways to invoke an action: +The recommended approach for any operation that involves changing data is to use a ``. +This ensures your application works even before any client-side JavaScript has loaded, providing a robust user experience through [progressive enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). -1. **Via the `` element's `action` prop**: This is the recommended approach for most data mutations, especially those triggered by user input, as it provides **progressive enhancement**. -2. **Programmatically with `useAction`**: For scenarios where you need to trigger an action outside of a form context. +For cases where a form is not suitable, you can use the `useAction` primitive to invoke the action programmatically. -### Handling Form Submissions with the `action` prop +### With the `` element -Solid Router extends the standard HTML `` element to accept an `action` prop, allowing you to handle your form submissions to an action. -This method provides the best user experience due to progressive enhancement. +Solid Router extends the standard HTML `` element to work with actions. +By passing an action to the `action` prop, you can handle form submissions seamlessly. -When using actions with ``: +Consider these points when using actions with ``: 1. The `` element **must** have `method="post"`. 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. -3. For SSR environments, you **must** provide a unique name string as the second parameter to the `action` function. +3. For SSR environments, you **must** provide a unique name as the second parameter to the `action` function. This name is used by Solid Router to uniquely identify and serialize the action across the client and server. ```tsx import { action } from "@solidjs/router"; const addPostAction = action(async (formData: FormData) => { - const title = formData.get("title")?.toString(); - - if (!title || title.trim() === "") { - return { success: false, message: "Post title cannot be empty." }; - } - - const response = await fetch("https://api.com/posts", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ title }), - }); - - if (!response.ok) { - const errorData = await response.json(); - return { success: false, message: errorData.message }; - } - - return { success: true }; + // ... }, "addPost"); function AddPostForm() { return ( -
@@ -120,10 +99,9 @@ function AddPostForm() { } ``` -When this form is submitted, `addPostFormAction` will be invoked with the `FormData` containing the form values. - -:::tip[File Uploads] +When the form is submitted, `addPostFormAction` will be invoked with the `FormData` containing the form values. +:::tip[Uploading files] If your form includes file inputs, ensure your `
` element has `enctype="multipart/form-data"` to correctly send the file data. ```tsx @@ -135,61 +113,39 @@ If your form includes file inputs, ensure your `` element has `enctype="mu ::: -#### Passing additional data +#### Passing additional arguments -Sometimes, your action might need additional data that isn't part of the form's inputs. -You can pass these additional arguments using the `with` method on your action. +Sometimes, your action needs data that isn't included in the form's inputs. +You can pass these additional arguments by using the `with` method. -Arguments passed to `with` will be forwarded to your action function before the `FormData` object. +The `with` method creates a new action that wraps around your original action. +When this new action is invoked, it passes the arguments specified in the `with` method to your original action function, followed by the `FormData` object. ```tsx import { action } from "@solidjs/router"; -const updatePostAction = action(async (postId: string, formData: FormData) => { - const newTitle = formData.get("title")?.toString(); +const editPostAction = action(async (postId: string, formData: FormData) => { + // ... +}, "editPost"); - if (!newTitle || newTitle.trim() === "") { - return { success: false, message: "Post title cannot be empty." }; - } - - const response = await fetch( - `https://api.com/posts/${encodeURIComponent(newTitle)}`, - { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ title: newTitle }), - } - ); - - if (!response.ok) { - const errorData = await response.json(); - return { success: false, message: errorData.message }; - } - - return { success: true }; -}); - -function PostEditForm(props: { postId: string }) { +function EditPostForm(props: { postId: string }) { return ( - - - - + + +
); } ``` -Here, `updatePostAction` receives `postId` (passed via `with`), and then the `formData` from the form. +In this example, `editPostAction` receives `postId` (passed via `with`), and then the `formData` from the form. -### Invoking Actions Programmatically with `useAction` +### With the `useAction` primitive -While forms are highly recommended for progressive enhancement, there are scenarios where you might need to trigger an action directly from a Solid component, outside of a `
` element. -The `useAction` primitive allows you to do this. +For scenarios where a `` is not suitable, the `useAction` primitive provides a function to invoke an action programmatically. +This approach requires client-side JavaScript and is not progressively enhanceable. -`useAction` takes an action as an argument and returns a function that, when called, will invoke the action with the provided parameters. +The `useAction` primitive takes an action as its argument and returns a function that, when called, will invoke the action with the provided parameters. ```tsx import { action, useAction } from "@solidjs/router"; @@ -201,20 +157,176 @@ const likePostAction = action(async (postId: string) => { }); function LikePostButton(props: { postId: string }) { - const likePost = useAction(); + const likePost = useAction(likePostAction); - return ; + return ; } ``` In this example, `likePost` is a function that can be called with arguments matching `likePostAction`. -When the button is clicked, `likePostAction` invokes for the specific post ID. +When the button is clicked, `likePostAction` is invoked for the specific `postId`. + +## Tracking submission state + +When an action is invoked, it creates a **submission** object. +This object is a snapshot of the action's execution, containing its input, current status (pending or complete), and its final result or error. +To access this state, Solid Router provides the `useSubmission` and `useSubmissions` primitives. + +The `useSubmission` primitive tracks the state of the _most recent_ submission for a specific action. +This is ideal for most use cases, such as disabling a form's submit button while the action is pending or displaying a confirmation message upon success. + +```tsx +import { Show } from "solid-js"; +import { action, useSubmission } from "@solidjs/router"; + +const saveSettingsAction = action(async (formData: FormData) => { + const email = formData.get("email")?.toString(); + + if (!email || !email.includes("@")) { + return { ok: false, message: "Please enter a valid email address." }; + } + + // ...send data to server + + return { ok: true }; +}, "save-settings"); + +function UserSettingsForm() { + const submission = useSubmission(saveSettingsAction); + + return ( + + + + + + +

Error: {submission.result.message}

+ + + + } + > +

Settings saved successfully!

+
+
+ ); +} +``` + +In this example, the form's submit button is disabled while `submission.pending` is true, and messages are shown based on the `submission.error` or `submission.result` properties. +The `clear` method resets the submission state, and `retry` re-executes the last submission with its original input. + +For more details, see the [`useSubmission` API reference](/solid-router/reference/data-apis/use-submission). + +:::tip +If you need to track multiple concurrent submissions for a single action, such as in a multi-file uploader interface, you can use the [`useSubmissions` primitive](/solid-router/reference/data-apis/use-submissions). +::: + +## Handling errors + +Actions provide two primary mechanisms for error handling. + +1. **Throwing an `Error`:** + You can `throw` an `Error` instance within an action. + When you do, Solid Router catches it and makes it available in the `submission.error` property. +2. **Returning a structured object:** + For more complex scenarios, like form validation, you can `return` an object from your action. + This object is then available in the `submission.result` property. + This allows you to pass detailed error information back to your component for display. + +While you can use either method as you see fit, a common pattern is to `throw` for unexpected errors and `return` an object for expected ones (like validation failures). + +```tsx +import { action, useSubmission } from "@solidjs/router"; +import { Show } from "solid-js"; + +const editPostAction = action(async (postId: string, formData: FormData) => { + const title = formData.get("title")?.toString(); + + // Validation errors are expected. + // We return an object to handle them gracefully. + if (!title || title.length < 3) { + return { + success: false, + errors: { title: "Title must be at least 3 characters long." }, + }; + } + + const response = await fetch(`https://api.com/posts/${postId}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title }), + }); + + if (!response.ok) { + // Server-side validation errors are also expected. + if (response.status === 400) { + const errorData = await response.json(); + return { + success: false, + errors: errorData, + }; + } + + // For other failures, we throw an error. + throw new Error("An unexpected error occurred. Please try again."); + } -## Query revalidation + return { success: true }; +}, "editPost"); + +function EditPostForm(props: { postId: string }) { + const submission = useSubmission(editPostAction); + const errors = () => submission.result?.errors; + + return ( +
+ + {(error) => ( +
+

{error().message}

+ +
+ )} +
+ +
+ + {(error) =>

{error()}

}
+
+ + +
+ ); +} +``` + +In this example: + +- If the title validation fails, the action returns an object with an `errors` field. + The UI then accesses `submission.result.errors.title` to display the specific validation message. +- If an unexpected server error occurs, the action throws an `Error`. + The UI uses `submission.error` to display a generic error message and provides a "Try Again" button. + +## Automatic data revalidation + +After you change data on the server, you face a common challenge: the data displayed in your UI is now stale. +To solve this, Solid Router automatically revalidates the data for the current route after a successful action. -By default, when an action successfully finishes its execution, Solid Router will automatically revalidate all queries on the same page. This means you typically don't have to manually refetch data after a mutation. -For example, if you add a new post using an action, any query on that page fetching a list of posts will automatically re-run and update your UI. +Behind the scenes, Solid Router re-runs all active queries on the page, ensuring that any component using that data is automatically updated with the freshest information. + +For example, if you have a page that displays a list of posts and includes a form to add a new one, the list will automatically update after you submit the form. ```tsx import { query, action, createAsync } from "@solidjs/router"; @@ -252,111 +364,153 @@ function PostsPage() { } ``` -While automatic revalidation is convenient, there are times you need more control. -Solid Router's response helpers allow you to precisely manage which queries are revalidated, or even disable revalidation entirely for a specific action. -You'll learn more about these helpers in the next section. +While this automatic behavior is convenient for most cases, you may need more fine-grained control. +The next section on managing navigation and revalidation explains how you can customize or even disable this behavior for specific actions. + +## Managing navigation and revalidation -## Response helpers +While automatic revalidation is powerful, you often need more control. +You might want to redirect the user to a different page, prevent revalidation entirely, or revalidate a specific set of queries. +This is where response helpers come in. -Solid Router provides special response helpers that enable your actions to explicitly dictate routing and query revalidation: +Response helpers are functions that create special `Response` objects. +When an action returns or throws one of these responses, Solid Router intercepts it and performs a specific task. -- [`json`](/solid-router/reference/response-helpers/json): Allows customizing query revalidation behavior when the action returns something. - While it's not necessary to use this helper to return a value from an action (a plain JavaScript value also works), the `json` helper can be used when you want to return data **and** customize query revalidation. -- [`redirect`](/solid-router/reference/response-helpers/redirect): Performs a redirect. -- [`reload`](/solid-router/reference/response-helpers/reload): Revalidates queries. +### Redirecting -You can either `return` or `throw` the result of a response helper in order for Solid Router to handle it. -Throwing might be preferable in TypeScript environemnts since it doesn't conflict with TypeScript's type system. +If you want to navigate the user to a new page after an action completes, you can use the [`redirect`](/solid-router/reference/response-helpers/redirect) helper. ```tsx -import { action, redirect, json, reload } from "@solidjs/router"; +import { action, redirect } from "@solidjs/router"; -// Example 1: Redirecting after a successful login const loginAction = action(async (formData: FormData) => { - // ...Login logic - + // ...login logic throw redirect("/dashboard"); }); +``` + +### Customizing revalidation + +To override the default revalidation behavior, you can use the [`reload`](/solid-router/reference/response-helpers/reload) and [`json`](/solid-router/reference/response-helpers/json) helpers. + +- `reload`: Use this when you only need to control revalidation. +- `json`: Use this when you need to control revalidation _and_ return data from the action. -// Example 2: Returning data with specific revalidation control +Both helpers accept a `revalidate` option, which takes an array of query keys to revalidate. +If you provide an empty array (`[]`), you can prevent revalidation altogether. + +```tsx +import { action, reload, json } from "@solidjs/router"; + +// Example 1: Revalidating a specific query const savePreferencesAction = action(async (formData: FormData) => { - // ...Save preferences logic + // ...save preferences logic - return json( - { success: true, message: "Preferences saved!" }, - { revalidate: ["userPreferences"] } - ); + // Only revalidate the 'userPreferences' query + throw reload({ revalidate: ["userPreferences"] }); }); -// Example 3: Disabling revalidation +// Example 2: Disabling revalidation and returning data const logActivityAction = action(async (activityData: FormData) => { - // ...Log activity to server + // ...log activity to server - // Don't revalidate any queries on the current page for this action - throw reload({ revalidate: [] }); + // Return a success message without revalidating any queries + return json({ success: true }, { revalidate: [] }); }); ``` -## Tracking action state +:::tip[Throwing vs. Returning] +You can either `return` or `throw` a response helper. +In TypeScript, `throw` can be more convenient, as it avoids potential type conflicts with your action's expected return value. +::: + +## Optimistic UI -When an action is executing, your UI often needs to reflect its current state. -Solid Router provides the `useSubmission` and `useSubmissions` primitives to track the lifecycle of your actions. +Optimistic UI is a pattern where you update the user interface immediately after a user performs an operation. +This is done without waiting for the server to confirm the operation's success. +This approach makes your application feel faster and more responsive. +If the operation fails, the UI change is reverted. -The `useSubmission` hook returns an object with reactive values that represent the state of the _most recent_ submission for a given action. -This is perfect for displaying pending states on buttons, showing the latest error, or displaying the outcome of the action. +Actions can be combined with local state management to implement optimistic UI. +You can use the `useSubmission` primitive to access the input of an action as it's being submitted. +This input can be used to temporarily update your UI. ```tsx -import { Show } from "solid-js"; -import { action, useSubmission } from "@solidjs/router"; +import { For, createSignal, Show } from "solid-js"; +import { query, action, createAsync, useSubmission } from "@solidjs/router"; -const saveSettingsAction = action(async (formData: FormData) => { - const email = formData.get("email")?.toString(); +const getPostsQuery = query(async () => { + const response = await fetch("https://api.com/posts"); + return await response.json(); +}, "getPosts"); - if (!email || !email.includes("@")) { - throw new Error("Please enter a valid email address."); - } +const addPostAction = action(async (formData: FormData) => { + const title = formData.get("title")?.toString(); + + const response = await fetch("https://api.com/posts", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ title }), + }); - await new Promise((res) => setTimeout(res, 2000)); // Simulate API call + if (!response.ok) { + return { ok: false, message: "Failed to add post" }; + } - return { success: true, message: "Settings saved successfully!" }; -}, "save-settings"); + return { ok: true }; +}, "addPost"); -function UserSettingsForm() { - const submission = useSubmission(saveSettingsAction); +function PostsPage() { + const postsQuery = createAsync(() => getPostsQuery()); + const submission = useSubmission(addPostAction); + + const optimisticPosts = () => { + const originalPosts = postsQuery() ?? []; + if (submission.pending) { + const formData = submission.input[0] as FormData; + const title = formData.get("title")?.toString(); + if (title) { + // Add the optimistic post to the list with a temporary ID. + return [...originalPosts, { id: -1, title }]; + } + } + return originalPosts; + }; return ( -
- - +
+

All Posts

+ {/* The list now uses optimisticPosts to render */} + {(post) =>

{post.title}

}
- +

Add New Post

+ + + + - {(error) => ( -
-

Error: {error().message}

- - -
- )} +

Failed to add post. Please try again.

- - {(result) => ( -

{result().message}

- )} -
- +
); } ``` -In this example, the form's submit button is disabled during `submission.pending`, and appropriate messages are shown based on `submission.error` or `submission.result`. -The `clear` method resets the submission state, and `retry` re-executes the last submission with its original input. +In this example, we create a derived signal `optimisticPosts`. +When an action is pending, it checks the `submission.input` and adds the new post to the list with a temporary ID. +This makes the new post appear in the list instantly. -For more details, see the [`useSubmission` API reference](/solid-router/reference/data-apis/use-submission). +If the action fails, `submission.pending` becomes false, and `optimisticPosts` will revert to showing the original list from `postsQuery`. +The error message will also be displayed. -:::tip -If you need to track multiple concurrent or recent submissions for an action (e.g., a list of file uploads, a queue of items being processed), the [`useSubmissions` primitive](/solid-router/reference/data-apis/use-submissions) can be used. +When the action succeeds, Solid Router automatically revalidates `getPostsQuery`. +This fetches the updated list of posts from the server, including the newly created post with its final ID. +The UI then seamlessly updates to reflect the correct state. + +:::note +For more advanced optimistic UI patterns, consider using a library like [TanStack Query](https://tanstack.com/query/latest/docs/framework/solid/guides/optimistic-updates). +It provides robust tools for managing server state, including complex optimistic updates. ::: From e08e0373176a5c9c07c9ad5ded95a1896e7f1202 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Sat, 9 Aug 2025 07:15:29 +0330 Subject: [PATCH 08/32] update --- src/routes/solid-router/concepts/actions.mdx | 259 ++++++++----------- 1 file changed, 110 insertions(+), 149 deletions(-) diff --git a/src/routes/solid-router/concepts/actions.mdx b/src/routes/solid-router/concepts/actions.mdx index 4dd0620a6..0e4e117b6 100644 --- a/src/routes/solid-router/concepts/actions.mdx +++ b/src/routes/solid-router/concepts/actions.mdx @@ -17,24 +17,25 @@ description: >- isomorphic data flows with progressive enhancement support. --- -Actions simplify handling data mutations and side effects in your applications. -They are ideal for handling form submissions, API calls, and other updates that modify your application's state on the server or client. +In web applications, many user interactions result in changing data on the server. +These changes, often called mutations, require careful management of asynchronous operations, state updates, and error handling. +Actions provide a comprehensive framework for managing these data mutations. +They are designed to handle everything from simple form submissions to complex asynchronous workflows. Actions provide several benefits: - **Integrated state management:** - Solid Router automatically tracks the execution state of an action (pending, successful, error), simplifying reactive UI feedback. + Solid Router automatically tracks the execution state of an action, simplifying reactive UI feedback. - **Automatic data revalidation:** - After an action successfully completes, Solid Router revalidates relevant queries that are used on the same page. - This ensures your UI reflects the latest data without manual intervention. + After an action successfully completes, Solid Router revalidates relevant queries, ensuring the UI reflects the latest data. - **Progressive enhancement:** When used with HTML forms, actions enable functionality even if JavaScript is not yet loaded. ## Defining actions -At their core, actions are **asynchronous operations**. -You define them by wrapping your asynchronous logic with the [`action`](/solid-router/reference/data-apis/action) function. -When the action is invoked, Solid Router executes your defined logic. +At their core, actions are asynchronous operations. +They are defined by wrapping asynchronous logic with the [`action`](/solid-router/reference/data-apis/action) function. +When the action is invoked, Solid Router executes the defined logic. ```tsx import { action } from "@solidjs/router"; @@ -57,52 +58,50 @@ const addPostAction = action(async (title: string) => { }); ``` -In this example, `addPostAction` sending a POST request to create a new post. -If the request failed, it returns an error. -We will cover error handling later in this page. +In this example, an action is defined that sends a POST request to create a new post. ## Invoking actions -You can invoke an action in two primary ways: through an HTML `
` or programmatically with the `useAction` primitive. +Actions can be invoked in two primary ways: through an HTML `` or programmatically with the `useAction` primitive. -The recommended approach for any operation that involves changing data is to use a ``. -This ensures your application works even before any client-side JavaScript has loaded, providing a robust user experience through [progressive enhancement](https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement). +The recommended approach for is to use a ``. +This ensures the application works even before any client-side JavaScript has loaded, providing a robust user experience through progressive enhancement. -For cases where a form is not suitable, you can use the `useAction` primitive to invoke the action programmatically. +For cases where a form is not suitable, the [`useAction` primitive](/solid-router/reference/data-apis/use-action) can be used to invoke the action programmatically. ### With the `` element Solid Router extends the standard HTML `` element to work with actions. -By passing an action to the `action` prop, you can handle form submissions seamlessly. +By passing an action to the `action` prop, form submissions can be handled seamlessly. Consider these points when using actions with ``: 1. The `` element **must** have `method="post"`. 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. -3. For SSR environments, you **must** provide a unique name as the second parameter to the `action` function. +3. For SSR environments, a unique name **must** be provided as the second parameter to the `action` function. This name is used by Solid Router to uniquely identify and serialize the action across the client and server. ```tsx import { action } from "@solidjs/router"; const addPostAction = action(async (formData: FormData) => { - // ... + // ... Handles the form submission logic. }, "addPost"); function AddPostForm() { return ( - +
); } ``` -When the form is submitted, `addPostFormAction` will be invoked with the `FormData` containing the form values. +In this example, when the form is submitted, `addPostAction` will be invoked with the `FormData` containing the form values. :::tip[Uploading files] -If your form includes file inputs, ensure your `
` element has `enctype="multipart/form-data"` to correctly send the file data. +If a form includes file inputs, the `` element must have `enctype="multipart/form-data"` to correctly send the file data. ```tsx @@ -115,17 +114,17 @@ If your form includes file inputs, ensure your `` element has `enctype="mu #### Passing additional arguments -Sometimes, your action needs data that isn't included in the form's inputs. -You can pass these additional arguments by using the `with` method. +Sometimes, an action needs data that isn't included in the form's inputs. +These additional arguments can be passed by using the `with` method. -The `with` method creates a new action that wraps around your original action. -When this new action is invoked, it passes the arguments specified in the `with` method to your original action function, followed by the `FormData` object. +The `with` method creates a new action that wraps around the original action. +When this new action is invoked, it passes the arguments specified in the `with` method to the original action function, followed by the `FormData` object. ```tsx import { action } from "@solidjs/router"; const editPostAction = action(async (postId: string, formData: FormData) => { - // ... + // ... Handles the edit logic. }, "editPost"); function EditPostForm(props: { postId: string }) { @@ -151,9 +150,7 @@ The `useAction` primitive takes an action as its argument and returns a function import { action, useAction } from "@solidjs/router"; const likePostAction = action(async (postId: string) => { - await fetch(`https://api.com/posts/${encodeURIComponent(postId)}/likes`, { - method: "POST", - }); + // ... Likes a post on the server. }); function LikePostButton(props: { postId: string }) { @@ -172,7 +169,7 @@ When an action is invoked, it creates a **submission** object. This object is a snapshot of the action's execution, containing its input, current status (pending or complete), and its final result or error. To access this state, Solid Router provides the `useSubmission` and `useSubmissions` primitives. -The `useSubmission` primitive tracks the state of the _most recent_ submission for a specific action. +The [`useSubmission` primitive](/solid-router/reference/data-apis/use-submission) tracks the state of the _most recent_ submission for a specific action. This is ideal for most use cases, such as disabling a form's submit button while the action is pending or displaying a confirmation message upon success. ```tsx @@ -186,7 +183,7 @@ const saveSettingsAction = action(async (formData: FormData) => { return { ok: false, message: "Please enter a valid email address." }; } - // ...send data to server + // ... Sends the settings data to the server. return { ok: true }; }, "save-settings"); @@ -202,105 +199,79 @@ function UserSettingsForm() { {submission.pending ? "Saving..." : "Save Settings"} - -

Error: {submission.result.message}

- - - - } - > -

Settings saved successfully!

+ +

Error: {submission.result.message}

); } ``` -In this example, the form's submit button is disabled while `submission.pending` is true, and messages are shown based on the `submission.error` or `submission.result` properties. -The `clear` method resets the submission state, and `retry` re-executes the last submission with its original input. +In this example, the form's submit button is disabled while `submission.pending` is true. +Error messages are shown based on the `submission.result` property. For more details, see the [`useSubmission` API reference](/solid-router/reference/data-apis/use-submission). :::tip -If you need to track multiple concurrent submissions for a single action, such as in a multi-file uploader interface, you can use the [`useSubmissions` primitive](/solid-router/reference/data-apis/use-submissions). +To track multiple submissions for a single action, such as in a multi-file uploader interface, use the [`useSubmissions` primitive](/solid-router/reference/data-apis/use-submissions). ::: ## Handling errors -Actions provide two primary mechanisms for error handling. +When an action performs an operation like an API call, it can fail for various reasons. +A robust application must handle these failures gracefully. +Solid Router provides two mechanisms for an action to signal its outcome: returning a value or throwing an `Error`. -1. **Throwing an `Error`:** - You can `throw` an `Error` instance within an action. - When you do, Solid Router catches it and makes it available in the `submission.error` property. -2. **Returning a structured object:** - For more complex scenarios, like form validation, you can `return` an object from your action. - This object is then available in the `submission.result` property. - This allows you to pass detailed error information back to your component for display. +Throwing an `Error` is a valid way to signal failure. +Solid Router will catch the thrown error and make it available in the `submission.error` property. +However, this approach has some drawbacks. +The `submission.error` property is typed as `any`, which undermines type safety in the consuming component. +It is also difficult to convey structured error information, such as validation messages for multiple form fields, using a simple `Error` instance. -While you can use either method as you see fit, a common pattern is to `throw` for unexpected errors and `return` an object for expected ones (like validation failures). +For these reasons, the recommended practice is to always `return` a descriptive object from an action to represent its outcome. +The returned object is available in the `submission.result` property, which will be fully typed. +This makes handling different outcomes in the UI simple and safe. ```tsx -import { action, useSubmission } from "@solidjs/router"; import { Show } from "solid-js"; +import { action, useSubmission } from "@solidjs/router"; -const editPostAction = action(async (postId: string, formData: FormData) => { +const editPostAction = action(async (formData: FormData) => { const title = formData.get("title")?.toString(); - // Validation errors are expected. - // We return an object to handle them gracefully. if (!title || title.length < 3) { return { - success: false, + ok: false, errors: { title: "Title must be at least 3 characters long." }, }; } - const response = await fetch(`https://api.com/posts/${postId}`, { - method: "PATCH", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ title }), - }); - - if (!response.ok) { - // Server-side validation errors are also expected. - if (response.status === 400) { - const errorData = await response.json(); - return { - success: false, - errors: errorData, - }; - } + // ... Performs an update on the server and handles potential errors. - // For other failures, we throw an error. - throw new Error("An unexpected error occurred. Please try again."); - } - - return { success: true }; + return { ok: true }; }, "editPost"); function EditPostForm(props: { postId: string }) { const submission = useSubmission(editPostAction); - const errors = () => submission.result?.errors; + + const errors = () => { + const result = submission.result; + if (result && !result.ok) { + return result.errors; + } + }; return (
- - {(error) => ( -
-

{error().message}

- -
- )} + +

{errors().server}

- {(error) =>

{error()}

}
+ +

{errors().title}

+
- - -

Failed to add post. Please try again.

-
); } ``` -In this example, we create a derived signal `optimisticPosts`. +In this example, a derived signal `optimisticPosts` is created. When an action is pending, it checks the `submission.input` and adds the new post to the list with a temporary ID. This makes the new post appear in the list instantly. @@ -513,4 +474,4 @@ The UI then seamlessly updates to reflect the correct state. :::note For more advanced optimistic UI patterns, consider using a library like [TanStack Query](https://tanstack.com/query/latest/docs/framework/solid/guides/optimistic-updates). It provides robust tools for managing server state, including complex optimistic updates. -::: +::: \ No newline at end of file From d6f600001d2768eaa80ebaff7d8d818f3eb0e9d9 Mon Sep 17 00:00:00 2001 From: "Amir H. Hashemi" <87268103+amirhhashemi@users.noreply.github.com> Date: Thu, 14 Aug 2025 18:32:46 +0330 Subject: [PATCH 09/32] update --- src/routes/solid-router/concepts/actions.mdx | 213 +++++++------ src/routes/solid-router/concepts/data.json | 3 +- src/routes/solid-router/concepts/queries.mdx | 292 ++++++++++++++++++ .../reference/data-apis/action.mdx | 163 ++-------- .../reference/data-apis/query.mdx | 170 ++++------ .../reference/data-apis/use-action.mdx | 34 +- .../reference/data-apis/use-submission.mdx | 103 +++--- .../reference/data-apis/use-submissions.mdx | 98 +++--- 8 files changed, 614 insertions(+), 462 deletions(-) create mode 100644 src/routes/solid-router/concepts/queries.mdx diff --git a/src/routes/solid-router/concepts/actions.mdx b/src/routes/solid-router/concepts/actions.mdx index 0e4e117b6..2f37c6cf2 100644 --- a/src/routes/solid-router/concepts/actions.mdx +++ b/src/routes/solid-router/concepts/actions.mdx @@ -17,17 +17,16 @@ description: >- isomorphic data flows with progressive enhancement support. --- -In web applications, many user interactions result in changing data on the server. -These changes, often called mutations, require careful management of asynchronous operations, state updates, and error handling. -Actions provide a comprehensive framework for managing these data mutations. -They are designed to handle everything from simple form submissions to complex asynchronous workflows. +Many user interactions in an application involve changing data on the server. +These **mutations** can be challenging to manage, as they require updates to the application's state and proper error handling. +Actions provide a simple solution for managing these data mutations. Actions provide several benefits: - **Integrated state management:** Solid Router automatically tracks the execution state of an action, simplifying reactive UI feedback. - **Automatic data revalidation:** - After an action successfully completes, Solid Router revalidates relevant queries, ensuring the UI reflects the latest data. + After an action successfully completes, Solid Router revalidates relevant [`queries`](/solid-router/concepts/queries), ensuring the UI reflects the latest data. - **Progressive enhancement:** When used with HTML forms, actions enable functionality even if JavaScript is not yet loaded. @@ -40,13 +39,11 @@ When the action is invoked, Solid Router executes the defined logic. ```tsx import { action } from "@solidjs/router"; -const addPostAction = action(async (title: string) => { - const response = await fetch("https://api.com/posts", { +const createTicketAction = action(async (subject: string) => { + const response = await fetch("https://api.com/support/tickets", { method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ title }), + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ subject }), }); if (!response.ok) { @@ -55,16 +52,16 @@ const addPostAction = action(async (title: string) => { } return { ok: true }; -}); +}, "createTicket"); ``` -In this example, an action is defined that sends a POST request to create a new post. +In this example, an action is defined that creates a support ticket with a subject. ## Invoking actions Actions can be invoked in two primary ways: through an HTML `
` or programmatically with the `useAction` primitive. -The recommended approach for is to use a ``. +The recommended approach is to use a ``. This ensures the application works even before any client-side JavaScript has loaded, providing a robust user experience through progressive enhancement. For cases where a form is not suitable, the [`useAction` primitive](/solid-router/reference/data-apis/use-action) can be used to invoke the action programmatically. @@ -84,21 +81,25 @@ Consider these points when using actions with ``: ```tsx import { action } from "@solidjs/router"; -const addPostAction = action(async (formData: FormData) => { - // ... Handles the form submission logic. -}, "addPost"); +const submitFeedbackAction = action(async (formData: FormData) => { + const message = formData.get("message")?.toString(); + + // ... Sends the feedback to the server. + + return { ok: true }; +}, "submitFeedback"); -function AddPostForm() { +function FeedbackForm() { return ( - - - + +