Действия и формы
Действие (action) — функция, которую модуль маршрута экспортирует под именем action. Она выполняется на сервере, когда на адрес маршрута отправляют форму методом POST (или PUT, PATCH, DELETE). Отправляют такую форму компонентом Form — это обычный тег form, которому фреймворк добавляет отправку без перезагрузки страницы.
Функция сохранения
Допишите в app/data.server.ts функцию добавления статьи:
// app/data.server.ts
export async function createPost(title: string, body: string) {
const post: Post = { id: posts.length + 1, title, body };
posts.push(post);
return post;
}Страница с формой
Добавьте маршрут route("posts/new", "routes/new-post.tsx") и создайте файл:
// app/routes/new-post.tsx
import { Form, redirect } from "react-router";
import type { Route } from "./+types/new-post";
import { createPost } from "../data.server";
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const title = String(formData.get("title"));
const body = String(formData.get("body"));
const post = await createPost(title, body);
return redirect(`/posts/${post.id}`);
}
export default function NewPost() {
return (
<Form method="post">
<input name="title" placeholder="Заголовок" />
<textarea name="body" placeholder="Текст статьи" />
<button type="submit">Опубликовать</button>
</Form>
);
}Откройте http://localhost:5173/posts/new, заполните поля и нажмите «Опубликовать». Форма уйдёт на тот же адрес, сработает action, и redirect откроет страницу новой статьи. Вернувшись на главную, вы увидите статью в списке.
Как это работает
Formперехватывает отправку и посылает данные на сервер запросом POST.- На сервере вызывается
action,request.formData()возвращает поля формы по атрибутамname. - После
actionфреймворк заново вызывает загрузчики страницы — данные на экране всегда свежие. redirectизreact-routerотвечает переходом на другой адрес.
Без JavaScript форма тоже работает: браузер отправит её обычным способом, а сервер ответит перенаправлением.
Ссылка на форму
На главной добавьте ссылку на новую страницу:
// app/routes/home.tsx
<Link to="/posts/new">Написать статью</Link>Форма на другой адрес
Атрибут action у Form указывает, какой маршрут обработает отправку. Например, кнопку удаления можно разместить на странице статьи:
// app/routes/post.tsx
<Form method="post" action={`/posts/${post.id}/delete`}>
<button type="submit">Удалить</button>
</Form>Нужен маршрут posts/:postId/delete с функцией action; компонент ему не обязателен.
Частая ошибка
Поле без атрибута name не попадает в formData: formData.get("title") вернёт null, а String(null) превратится в строку "null" — именно она и окажется заголовком статьи. Проверяйте, что у каждого поля есть name, совпадающее с ключом, который читает action.