Концепция action в React Router

В React Router action является ключевым инструментом для обработки действий, инициируемых пользователем, которые изменяют состояние приложения, но не связаны напрямую с отображением маршрута. В отличие от loader, который загружает данные перед рендерингом маршрута, action используется для обработки POST-запросов, форм, модификации данных и других побочных эффектов.


Основы использования action

Каждый маршрут может иметь свой собственный action:

import { createBrowserRouter, RouterProvider } from "react-router-dom";
import App from "./App";
import { createPostAction } from "./actions";

const router = createBrowserRouter([
  {
    path: "/posts/new",
    element: <App />,
    action: createPostAction,
  },
]);

function Root() {
  return <RouterProvider router={router} />;
}

Здесь createPostAction — это функция, которая будет вызвана, когда пользователь отправляет данные формы на маршрут /posts/new.


Сигнатура функции action

Функция action получает объект с несколькими важными параметрами:

export async function createPostAction({ request, params }) {
  const formData = await request.formData();
  const title = formData.get("title");
  
  // Логика создания поста
  await savePost({ title });
  
  return redirect("/posts");
}

Ключевые элементы параметров:

  • request — объект запроса (аналог Request в Fetch API). Содержит метод, тело запроса, заголовки.
  • params — объект с динамическими сегментами URL маршрута.
  • context — (необязательный) объект для передачи дополнительных данных из родительских маршрутов.

Асинхронность и работа с данными

action может быть асинхронной функцией, что позволяет выполнять запросы к серверу, сохранять данные в базу, вызывать API. Пример асинхронного action:

export async function loginAction({ request }) {
  const formData = await request.formData();
  const email = formData.get("email");
  const password = formData.get("password");

  const response = await fetch("/api/login", {
    method: "POST",
    body: JSON.stringify({ email, password }),
    headers: { "Content-Type": "application/json" },
  });

  if (!response.ok) {
    return { error: "Неверные данные" };
  }

  return redirect("/dashboard");
}

Важный момент: action не рендерит компонент напрямую, а возвращает результат. Этот результат может быть:

  • Объект с данными, которые будут доступны через хук useActionData.
  • Перенаправление с помощью функции redirect.
  • Ошибка для обработки через errorElement.

Связь action и форм

React Router тесно интегрирован с компонентом <Form>. Любая форма, указывающая маршрут, будет автоматически вызывать action этого маршрута при сабмите:

import { Form, useActionData } from "react-router-dom";

export default function NewPost() {
  const actionData = useActionData();

  return (
    <Form method="post">
      <input name="title" placeholder="Название поста" />
      <button type="submit">Создать</button>
      {actionData?.error && <p>{actionData.error}</p>}
    </Form>
  );
}

Особенности:

  • Атрибут method формы (post, put, delete) соответствует типу запроса, передаваемого в action.
  • После отправки формы React Router вызывает соответствующий action без перезагрузки страницы.
  • Результат action доступен через хук useActionData, что упрощает обработку ошибок и отображение сообщений пользователю.

Работа с редиректами

Функция redirect используется для перенаправления после успешного выполнения действия:

import { redirect } from "react-router-dom";

export async function deletePostAction({ params }) {
  await deletePost(params.id);
  return redirect("/posts");
}

Это позволяет чисто отделить логику обработки действия от UI, избегая манипуляций с состоянием внутри компонентов.


Обработка ошибок

Если внутри action происходит ошибка, её можно передать в errorElement маршрута:

export async function riskyAction({ request }) {
  try {
    // Некоторая логика
  } catch (error) {
    throw new Response("Ошибка выполнения действия", { status: 500 });
  }
}

Маршрут с errorElement:

{
  path: "/risky",
  element: <RiskyPage />,
  action: riskyAction,
  errorElement: <ErrorPage />
}

React Router автоматически отобразит компонент ErrorPage, если action выбросит ошибку.


Передача контекста через вложенные маршруты

action маршрута может использовать данные из родительских маршрутов через context. Это удобно для многослойных форм или сложных процессов авторизации:

const router = createBrowserRouter([
  {
    path: "/dashboard",
    element: <DashboardLayout />,
    context: { userRole: "admin" },
    children: [
      {
        path: "settings",
        element: <Settings />,
        action: settingsAction,
      }
    ]
  }
]);

Внутри settingsAction можно получить context.userRole, чтобы проверить права доступа перед сохранением настроек.


Ключевые моменты концепции action

  • action предназначен для обработки пользовательских действий, влияющих на состояние данных.
  • Работает в связке с компонентом <Form> для автоматического сабмита.
  • Может быть асинхронной, возвращать данные или редиректы.
  • Поддерживает обработку ошибок через errorElement.
  • Обеспечивает чистое разделение логики действий и рендеринга UI.
  • Позволяет использовать контекст родительских маршрутов для передачи информации между уровнями приложения.

action в React Router является фундаментальным инструментом для построения интерактивных, динамичных и безопасных форм и маршрутов, где каждый пользовательский ввод обрабатывается централизованно и предсказуемо.