توابع سرور (Server Functions) که در اکوسیستمهای مدرن مثل Next.js به عنوان Server Actions نیز شناخته میشوند، توابعی ناهمگام (async) هستند که روی سرور اجرا میشوند اما میتوان آنها را مستقیماً از سمت کلاینت (مرورگر) صدا زد. برای تبدیل یک تابع معمولی به تابع سرور، از دایرکتیو "use server" استفاده میشود.
این دایرکتیو را میتوان به دو روش پیادهسازی کرد: ۱. در سطح تابع: قرار دادن دایرکتیو در اولین خط از بدنه یک تابع. ۲. در سطح فایل: قرار دادن دایرکتیو در بالاترین خط یک فایل جداگانه (در این حالت، تمام توابع Export شده از آن فایل، تابع سرور خواهند بود).
نحوه تعریف Server Functionها در فایلهای مجزا و کامپوننتهای سرور
فرآیند فراخوانی و وارد کردن (Import) آنها در کامپوننتهای کلاینت
مزیت افزایش تدریجی (Progressive Enhancement) در فرمها
شما میتوانید توابع سرور را مستقیماً به صورت داخلی (Inline) درون کامپوننتهای سرور تعریف کنید. کافی است عبارت 'use server' را در بالاترین خط بدنه تابع قرار دهید:
// app/page.tsx (یک Server Component)
export default function Page() {
// تعریف داخلی یک Server Action
async function createPost(formData: FormData) {
'use server'
const title = formData.get('title')
const content = formData.get('content')
// ۱. عملیات روی دیتابیس (Mutation)
// ۲. بهروزرسانی کش (Revalidate cache)
}
return (
<form action={createPost}>
<input type="text" name="title" />
<textarea name="content" />
<button type="submit">ایجاد پست</button>
</form>
)
}
💡 قابلیت Progressive Enhancement (افزایش تدریجی): > کامپوننتهای سرور به صورت پیشفرض از این قابلیت پشتیبانی میکنند. این یعنی حتی اگر جاوااسکریپت مرورگر کاربر هنوز کاملاً لود نشده باشد یا کاربر آن را غیرفعال کرده باشد، فرمها پس از سابمیت همچنان کار میکنند و اطلاعات را به سرور میفرستند!
یک قانون مهم: شما نمیتوانید یک تابع سرور را به صورت داخلی (Inline) درون یک کامپوننت کلاینت (که بالای آن 'use client' قرار دارد) تعریف کنید.
برای استفاده از توابع سرور در کامپوننتهای کلاینت، باید ابتدا آنها را در یک فایل مجزا تعریف کرده، دایرکتیو 'use server' را در بالاترین خط آن فایل قرار دهید و سپس تابع را در فایل کلاینت خود import کنید.
// app/lib/actions.ts
'use server' // تمام توابع این فایل روی سرور اجرا میشوند
import { auth } from '@/lib/auth'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) {
throw new Error('Unauthorized')
}
const title = formData.get('title')
const content = formData.get('content')
// عملیات ذخیرهسازی...
}
export async function deletePost(formData: FormData) {
const session = await auth()
if (!session?.user) {
throw new Error('Unauthorized')
}
const id = formData.get('id')
// عملیات حذف پس از بررسی سطح دسترسی...
}
حالا میتوانید این توابع را به راحتی وارد کامپوننتهای کلاینت کرده و به ویژگیهایی مثل action در فرم یا formAction در دکمهها پاس دهید:
// app/ui/button.tsx
'use client'
import { createPost } from '@/app/lib/actions'
export function Button() {
return (
<form action={createPost}>
<button type="submit">Create Post</button>
</form>
)
}
🧠 رفتار فرمهای کلاینت قبل از Hydration: > در کامپوننتهای کلاینت، اگر کاربر قبل از لود شدن کامل جاوااسکریپت (فاز Hydration) روی دکمه کلیک کند، React سابمیتهای فرم را در یک صف (Queue) نگه میدارد تا به محض فعال شدن جاوااسکریپت، آنها را اولویتبندی و اجرا کند. پس از پایان Hydration، فرم بدون رفرش شدن کل صفحه (Refresh-free) رفتار خواهد کرد.
یک راهکار منعطف دیگر این است که تابع سرور را در یک Server Component تعریف کرده و آن را به عنوان یک prop به کامپوننت کلاینت زیرمجموعه خود بفرستید.
// app/client-component.tsx
'use client'
interface ClientComponentProps {
updateItemAction: (formData: FormData) => Promise<void>
}
export default function ClientComponent({ updateItemAction }: ClientComponentProps) {
return (
<form action={updateItemAction}>
<input type="text" name="itemName" />
<button type="submit">بهروزرسانی آیتم</button>
</form>
)
}
// app/page.tsx (Server Component)
import ClientComponent from './client-component'
export default function Page() {
// تعریف تابع سرور
async function updateItem(formData: FormData) {
'use server'
const itemName = formData.get('itemName')
// بهروزرسانی دیتابیس...
}
// پاس دادن تابع به کامپوننت فرزند به عنوان پروپ
return <ClientComponent updateItemAction={updateItem} />
}
دایرکتیو "use server" مرز بین کد کلاینت و سرور را برای یک تابع یا فایل مشخص میکند.
توابع سرور را میتوان به صورت Inline در Server Componentها یا در فایلهای مجزا تعریف کرد.
تعریف داخلی (Inline) توابع سرور درون کامپوننتهای کلاینت ('use client') ممنوع است و باید از روش Import استفاده شود.
استفاده از توابع سرور در قالب فرمها، پایداری برنامه را از طریق Progressive Enhancement بالا میبرد؛ به طوری که فرمها حتی بدون جاوااسکریپت نیز قادر به ارسال داده هستند.
شما میتوانید توابع سرور را از لایه سرور به عنوان Props به کامپوننتهای کلاینت منتقل کنید.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://nextjs.org/docs/app