Пошук уроків, статей та іншого контенту
Розглянете призначення Server Actions, їхню серверну модель виконання та взаємодію з інтерфейсом.
Server Actions — це асинхронні функції, які виконуються на сервері, але можуть викликатися з React-інтерфейсу як звичайні функції.
Вони зручні для операцій, які:
змінюють дані;
обробляють відправлення форм;
виконують код, якого не повинно бути в браузері;
працюють із базою даних, файловою системою або секретами.
Server Action не надсилає реалізацію функції до браузера. Клієнт отримує лише механізм виклику, а сам код виконується на сервері.
React-компонент у браузері
│
│ виклик Server Action
▼
сервер виконує функцію
│
│ результат
▼
React оновлює інтерфейсServer Actions є частиною серверної моделі React і потребують інтеграції з фреймворком або інструментами збірки, які підтримують серверні функції. На практиці їх часто використовують у фреймворках на кшталт Next.js.
Для позначення серверної функції використовується директива:
"use server";Вона може бути розташована на початку окремого модуля:
// app/actions.js
"use server";
export async function saveProfile(formData) {
const name = formData.get("name");
// Цей код виконується на сервері
console.log("Збереження профілю:", name);
return {
success: true,
};
}У такому випадку всі експортовані функції цього модуля розглядаються як Server Actions.
Директиву також можна розмістити всередині функції:
export async function saveProfile(formData) {
"use server";
// Серверна логіка
}Окремий файл із "use server" зазвичай зручніший: серверні операції легше знаходити, тестувати й повторно використовувати.
Виклик Server Action виглядає для розробника майже як звичайний виклик функції, але фактично між компонентом і сервером існує мережевий кордон.
Наприклад:
const result = await saveProfile(formData);Це не означає, що saveProfile буде виконана в браузері. Типова послідовність така:
React або фреймворк визначає, що викликається серверна функція.
Аргументи серіалізуються.
Браузер надсилає запит на сервер.
Сервер виконує Server Action.
Результат серіалізується.
Клієнт отримує результат і може оновити інтерфейс.
Тому Server Action має обмеження:
аргументи повинні мати підтримуваний для серіалізації формат;
результат також повинен бути серіалізованим;
функція не може безпосередньо використовувати змінні, доступні лише в браузері;
код Server Action не повинен покладатися на DOM або window.
Одна з головних переваг Server Actions — інтеграція з HTML-формами.
Функцію можна передати у властивість action:
<form action={saveProfile}>
<input name="name" />
<button type="submit">Зберегти</button>
</form>Під час відправлення форми React передасть Server Action об’єкт FormData.
Це відрізняється від традиційного підходу, де потрібно було:
перехопити подію submit;
викликати event.preventDefault();
вручну зібрати значення полів;
виконати fetch;
обробити стан завантаження;
обробити помилку;
оновити інтерфейс.
Server Actions не скасовують мережевий запит. Вони лише дають декларативніший спосіб описати операцію та зв’язати її з інтерфейсом.
Нижче наведено приклад для React 19 та середовища, яке підтримує Server Actions, наприклад Next.js.
// app/actions.js
"use server";
export async function subscribeToNewsletter(previousState, formData) {
const email = formData.get("email");
if (typeof email !== "string" || !email.includes("@")) {
return {
status: "error",
message: "Введіть коректну електронну адресу.",
};
}
// Тут може бути запис до бази даних або виклик серверного сервісу.
console.log(`Підписка для: ${email}`);
return {
status: "success",
message: "Ви успішно підписалися на розсилку.",
};
}У цьому прикладі функція отримує два аргументи:
previousState — попередній стан операції;
formData — дані форми.
Перший аргумент потрібен через використання useActionState.
// app/NewsletterForm.jsx
"use client";
import { useActionState } from "react";
import { useFormStatus } from "react-dom";
import { subscribeToNewsletter } from "./actions";
const initialState = {
status: "idle",
message: "",
};
function SubmitButton() {
const { pending } = useFormStatus();
return (
<button type="submit" disabled={pending}>
{pending ? "Надсилання..." : "Підписатися"}
</button>
);
}
export default function NewsletterForm() {
const [state, formAction] = useActionState(
subscribeToNewsletter,
initialState
);
return (
<form action={formAction}>
<label htmlFor="email">Електронна пошта</label>
<input
id="email"
name="email"
type="email"
required
placeholder="you@example.com"
/>
<SubmitButton />
{state.message && (
<p aria-live="polite">
{state.message}
</p>
)}
</form>
);
}У цьому компоненті:
useActionState зберігає результат останнього виконання Server Action;
formAction передається у <form action={...}>;
useFormStatus повідомляє, що форма зараз відправляється;
кнопка блокується під час виконання операції;
повідомлення від сервера відображається в інтерфейсі.
useFormStatus потрібно використовувати в компоненті, який є нащадком форми. Саме тому кнопка винесена в окремий компонент SubmitButton.
У прикладі серверна функція виконується не в NewsletterForm.jsx, а на сервері:
console.log(`Підписка для: ${email}`);Це повідомлення з’явиться в серверному терміналі, а не в консолі браузера.
У Server Action можна розміщувати серверні операції, наприклад:
"use server";
export async function createOrder(formData) {
// Перевірка даних
// Перевірка поточного користувача
// Робота з базою даних
// Повернення результату
}Водночас код, який залежить від браузера, у Server Action використовувати не можна:
"use server";
export async function invalidAction() {
// Так робити не можна:
// window.localStorage.setItem("theme", "dark");
}Для браузерного стану або API браузера використовуйте клієнтський компонент.
Server Action сама по собі не є станом React. Вона лише виконує серверну операцію та повертає результат.
Інтерфейс повинен окремо визначити:
що показувати до початку операції;
що показувати під час виконання;
що показувати після успіху;
як відображати помилку.
Для цього React надає спеціальні механізми:
useActionState — зберігає результат дії;
useFormStatus — повідомляє про поточний стан відправлення форми;
звичайні useState та умовний рендеринг — для локальної поведінки компонента.
Наприклад, серверна функція може повертати стандартизований результат:
return {
status: "error",
message: "Не вдалося зберегти дані.",
};А компонент вирішує, як його показати:
{state.status === "error" && (
<p role="alert">{state.message}</p>
)}
{state.status === "success" && (
<p>{state.message}</p>
)}Так серверна логіка відповідає за результат операції, а компонент — за його візуальне представлення.
Server Actions та API endpoints розв’язують схожі задачі, але мають різну модель використання.
операція викликається безпосередньо з React-інтерфейсу;
потрібно обробити форму;
серверна операція належить конкретній частині застосунку;
не потрібно проєктувати окремий публічний API-контракт.
до операції звертаються різні клієнти;
потрібна інтеграція з мобільним застосунком або стороннім сервісом;
необхідний явний HTTP-контракт;
endpoint має бути незалежним від React.
Server Action не є способом зробити серверну функцію доступною для будь-якого клієнта у вигляді універсального API. Вона насамперед призначена для зв’язку серверної логіки з компонентами та формами React.
Між браузером і сервером передаються не довільні JavaScript-значення, а дані, які середовище може серіалізувати.
Безпечно передавати прості значення:
await updateUser({
name: "Олена",
age: 28,
tags: ["react", "javascript"],
});Для форм типовим аргументом є FormData:
export async function updateUser(formData) {
const name = formData.get("name");
}Не слід передавати:
DOM-вузли;
функції;
екземпляри класів із нестандартним станом;
об’єкти з циклічними посиланнями;
значення, які не підтримуються конкретною інтеграцією React і фреймворку.
Точний набір підтримуваних типів залежить від версії React та середовища виконання, тому для Server Actions варто використовувати прості серіалізовані дані.
Server Action не є приватною функцією лише тому, що вона виконується на сервері. Фреймворк створює механізм, через який її можна викликати з клієнта.
Тому кожна дія повинна:
перевіряти вхідні дані;
перевіряти автентифікацію користувача;
перевіряти права доступу;
не довіряти значенням із форми;
не повертати клієнту зайві конфіденційні дані.
Наприклад, приховування кнопки в інтерфейсі не є перевіркою дозволу:
{isAdmin && <button>Видалити</button>}Користувач може виконати запит іншим способом, тому перевірка ролі повинна виконуватися також усередині Server Action:
"use server";
export async function deleteReport(reportId) {
const user = await getCurrentUser();
if (!user) {
throw new Error("Потрібна автентифікація.");
}
if (user.role !== "admin") {
throw new Error("Недостатньо прав.");
}
// Видалення звіту після перевірки прав.
}Перевірка на сервері є обов’язковою, навіть якщо інтерфейс уже приховує недоступні елементи.
Server Actions добре підходять для серверних мутацій, але не кожна дія інтерфейсу повинна бути серверною.
Наприклад, відкриття меню або зміна локального значення форми не потребує Server Action:
function MenuButton() {
const [open, setOpen] = useState(false);
return (
<button onClick={() => setOpen((value) => !value)}>
{open ? "Закрити" : "Відкрити"}
</button>
);
}Server Action потрібна тоді, коли дія справді повинна виконуватися на сервері: зберегти дані, перевірити права, запустити серверну операцію або змінити стан ресурсу.
Для форми із Server Action корисно мислити такими станами:
Початковий стан — форма готова до введення.
Виконання — дані відправляються на сервер.
Успіх — операція завершилася, інтерфейс показує результат.
Помилка валідації — користувач отримує повідомлення про неправильні дані.
Помилка сервера — інтерфейс показує загальне повідомлення та не розкриває внутрішні деталі.
Такий поділ допомагає не змішувати серверну логіку з відображенням стану в компоненті.
Директива "use client" і "use server" мають різне призначення. Не потрібно розміщувати серверну реалізацію безпосередньо серед клієнтського коду.
Серверну логіку краще винести в модуль із "use server" і імпортувати Server Action у клієнтський компонент через можливості фреймворку.
Валідація в HTML або клієнтському компоненті покращує досвід користувача, але не захищає серверну операцію. Дані потрібно перевіряти повторно всередині Server Action.
useFormStatus поза формоюuseFormStatus отримує стан батьківської форми. Якщо викликати його в компоненті, який не є нащадком <form>, він не зможе відстежувати потрібне відправлення.
Server Action включає мережеву взаємодію. Навіть якщо виклик синтаксично схожий на звичайну функцію, він може мати затримку, завершитися помилкою мережі або вимагати стану завантаження.
Server Action не може отримати довільний об’єкт із браузера. Передавайте прості дані або FormData, а складні клієнтські об’єкти перетворюйте на серіалізований формат.
Результат Server Action передається назад до клієнта. Не повертайте паролі, секретні ключі, внутрішні stack trace або інші дані, які не потрібні інтерфейсу.
Server Action — це асинхронна функція, яка виконується на сервері.
Директива "use server" позначає серверний код.
Виклик із React-компонента проходить через мережевий кордон, навіть якщо виглядає як звичайний виклик функції.
Server Actions особливо зручні для обробки форм і серверних мутацій.
useActionState допомагає отримувати результат дії, а useFormStatus — відображати стан виконання.
Аргументи та результати повинні бути серіалізованими.
Автентифікацію, права доступу та валідацію потрібно перевіряти всередині Server Action.
Server Actions призначені для інтеграції React-інтерфейсу із серверною логікою, але не замінюють універсальні API для всіх клієнтів.