Skip to content

Commit 9a10fcb

Browse files
docs: align from-solid-router migration with the rewritten router guides
Preload examples start the query with void and read it through a memo instead of reading props.data as an awaited value; the pending-submissions example moves the optimistic store into the component, takes FormData, and explains why .onSubmit reverts on settle; links to the new Data page. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 9db9241 commit 9a10fcb

1 file changed

Lines changed: 48 additions & 28 deletions

File tree

src/routes/(6)migration/(3)from-solid-router.mdx

Lines changed: 48 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -298,16 +298,18 @@ import { defineRoute } from "@solidjs/router";
298298

299299
const userRoute = defineRoute({
300300
path: "/users/:id/:tab?",
301-
preload: ({ params }) => getUser(params.id),
302-
component: (props) => (
303-
<User user={props.data} id={props.params.id} tab={props.params.tab} />
304-
),
301+
preload: ({ params }) => void getUser(params.id),
302+
component: (props) => <User id={props.params.id} tab={props.params.tab} />,
305303
});
306304
```
307305

308306
In this example, `id` is `string` and `tab` is `string | undefined`.
309307
Parameters inherited from a parent remain available as `string | undefined`.
310308

309+
Note the `void` in the preload.
310+
In Router 1.x, `load` results were often read from the component through `createAsync`; in Router 2 the preload only starts the query, and the component reads the same query through a memo, as shown under [Replace async wrappers](#replace-async-wrappers).
311+
`props.data` still holds whatever `preload` returns, captured once when the route matches, which makes it a poor fit for a promise that depends on `params`.
312+
311313
Use a path witness when the component is declared in another module:
312314

313315
```tsx
@@ -447,34 +449,44 @@ const submission = useSubmission(addTodo);
447449
After:
448450

449451
```tsx
450-
import { createOptimisticStore } from "solid-js";
452+
import { For, createOptimisticStore } from "solid-js";
451453
import { action, useSubmissions } from "@solidjs/router";
452454

453-
const [todos, setTodos] = createOptimisticStore(() => getTodos(), []);
454-
455-
const addTodo = action(async (form: URLSearchParams) => {
456-
const todo = await saveTodo(form.get("title") ?? "");
457-
return todo;
458-
}, "add-todo").onSubmit((form) => {
459-
setTodos((items) => {
460-
items.push({
461-
id: "pending",
462-
title: form.get("title") ?? "",
463-
pending: true,
464-
});
465-
});
466-
});
455+
const addTodo = action(async (form: FormData) => {
456+
return saveTodo(String(form.get("title") ?? ""));
457+
}, "add-todo");
467458

468-
function TodoForm() {
459+
function Todos() {
460+
const [todos, setTodos] = createOptimisticStore(
461+
() => getTodos(),
462+
[] as Todo[]
463+
);
469464
const submissions = useSubmissions(addTodo);
470465
const latest = () => submissions.at(-1);
471466

467+
addTodo.onSubmit((form) => {
468+
setTodos((items) => {
469+
items.push({
470+
id: "pending",
471+
title: String(form.get("title") ?? ""),
472+
pending: true,
473+
});
474+
});
475+
});
476+
472477
return (
473-
<form action={addTodo} method="post">
474-
<input name="title" />
475-
<button>Save</button>
476-
<p>{latest()?.error?.message}</p>
477-
</form>
478+
<>
479+
<ul>
480+
<For each={todos}>
481+
{(todo) => <li class={{ pending: !!todo.pending }}>{todo.title}</li>}
482+
</For>
483+
</ul>
484+
<form action={addTodo} method="post">
485+
<input name="title" />
486+
<button>Save</button>
487+
<p>{latest()?.error?.message}</p>
488+
</form>
489+
</>
478490
);
479491
}
480492
```
@@ -485,9 +497,14 @@ form[aria-busy] button {
485497
}
486498
```
487499

500+
The three concerns that `submission.pending` used to cover are now split: the optimistic store shows the new row, `aria-busy` styles the form, and `useSubmissions` reports the error.
501+
`.onSubmit` runs inside the action's transaction, so the optimistic push reverts on its own when the action settles and `getTodos` revalidates.
502+
Registering it inside the component ties the hook to the component's lifetime.
503+
488504
Router 2 retains a submission only when the action produces a result or error.
489505
A void or metadata-only completion still reaches `.onSettled(...)`.
490506
Keep `method="post"` on action forms, and give a server-rendered client action a stable name.
507+
The rewritten [Data loading and mutations](/routing/solid-router/data#show-what-is-happening) page walks through this split with a running example.
491508

492509
### Move response helpers
493510

@@ -630,16 +647,19 @@ Move route options into a named `route` export:
630647

631648
```tsx
632649
// routes/blog/[id].tsx
650+
import { createMemo } from "solid-js";
633651
import { int, type RouteProps } from "@solidjs/router";
634652
import { defineFileRoute } from "@solidjs/router/fs";
653+
import { getPost } from "../../data/posts";
635654

636655
export const route = defineFileRoute("/blog/:id", {
637656
matchFilters: { id: int },
638-
preload: ({ params }) => getPost(params.id),
657+
preload: ({ params }) => void getPost(params.id),
639658
});
640659

641660
export default function Post(props: RouteProps<typeof route>) {
642-
return <h1>{props.data.title}</h1>;
661+
const post = createMemo(() => getPost(props.params.id));
662+
return <h1>{post().title}</h1>;
643663
}
644664
```
645665

@@ -669,7 +689,7 @@ After:
669689
const routes = defineRoutes([
670690
defineRoute({
671691
path: "/users/:id",
672-
preload: ({ params }) => getUser(params.id),
692+
preload: ({ params }) => void getUser(params.id),
673693
component: User,
674694
}),
675695
]);

0 commit comments

Comments
 (0)