هنگام کار با توابع سرور (Server Actions)، سناریوهای متداولی در پروژه پیش میآید؛ کارهایی مثل نشان دادن وضعیت در حال لود (Pending)، بهروزرسانی کش، ریدایرکت کردن کاربر یا مدیریت کوکیها. در این بخش، این الگوهای کاربردی را همراه با مثال بررسی میکنیم.
چطور با هوک useActionState وضعیت در حال انتظار (Pending) را مدیریت کنیم؟
تفاوت میان متدهای مدیریت کش (refresh در برابر revalidatePath) چیست؟
نحوه ریدایرکت ایمن کاربر و کار با کوکیها در لایه سرور به چه صورت است؟
چطور یک تابع سرور را به کمک هوک useEffect به صورت خودکار اجرا کنیم؟
وقتی یک تابع سرور در حال اجراست و با دیتابیس یا یک سرویس خارجی ارتباط برقرار میکند، باید به کاربر بازخورد مناسبی (مثل اسپینر لودینگ) نشان داد. ریآکت برای این کار هوک useActionState را در اختیار ما قرار میدهد که یک فلگ بولین به نام pending برمیگرداند.
برای تغییر وضعیت (Transition) به صورت ایمن، اکشن خروجی این هوک را درون متد startTransition اجرا میکنیم:
// app/ui/button.tsx
'use client'
import { useActionState, startTransition } from 'react'
import { createPost } from '@/app/actions'
import { LoadingSpinner } from '@/app/ui/loading-spinner'
export function Button() {
// آرگومان اول: تابع سرور، آرگومان دوم: مقدار اولیه استیت
const [state, action, pending] = useActionState(createPost, false)
return (
<button onClick={() => startTransition(action)}>
{pending ? <LoadingSpinner /> : 'ایجاد پست'}
</button>
)
}
گاهی اوقات پس از انجام یک فرآیند (Mutation)، نیاز دارید بدون تغییر آدرس (URL)، دادههای صفحه فعلی را کاملاً تازهسازی کنید تا جدیدترین تغییرات روی UI بنشینند. برای این کار میتوانید متد refresh() را از پکیج next/cache درون تابع سرور صدا بزنید:
// app/lib/actions.ts
'use server'
import { auth } from '@/lib/auth'
import { refresh } from 'next/cache'
export async function updatePost(formData: FormData) {
const session = await auth()
if (!session?.user) throw new Error('Unauthorized')
// عملیات ویرایش دادهها...
// روتر کلاینت را رفرش میکند تا UI جدیدترین دیتا را بگیرد
refresh()
}
💡 نکته: متد
refresh()تگهای کششده دیتای شما را معتبرسازی مجدد (Revalidate) نمیکند. اگر از دیتای تگشده استفاده میکنید، باید به جای آن ازupdateTagیاrevalidateTagبهره ببرید.
در فریمورک Next.js، صفحات به شدت کش میشوند. وقتی تغییری ایجاد میکنید، برای اینکه صفحات دیگر (مثلاً لیست پستها در مسیر /posts) از این تغییر باخبر شوند، باید کش آن مسیر را به کمک revalidatePath یا کش یک تگ خاص را با revalidateTag پاک کنید:
// app/lib/actions.ts
'use server'
import { auth } from '@/lib/auth'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const session = await auth()
if (!session?.user) throw new Error('Unauthorized')
// عملیات ایجاد پست...
// به Next.js دستور میدهد کش مسیر /posts را منقضی و دوباره آپدیت کند
revalidatePath('/posts')
}
فرستادن کاربر به یک صفحه دیگر (مثلاً هدایت به صفحه مقالات بعد از ثبت موفقیتآمیز یک پست) با متد redirect انجام میشود.
// app/lib/actions.ts
'use server'
import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
export async function createPost(formData: FormData) {
// عملیات ثبت پست...
// ابتدا کش را پاک کنید تا صفحه مقصد دیتای تازه داشته باشد
revalidatePath('/posts')
// هدایت کاربر به صفحه جدید
redirect('/posts')
}
⚠️ یک نکته بسیار مهم: متد
redirectیک خطای کنترلجریان (Control-flow Exception) اختصاصی در لایه فریمورک پرتاب (throw) میکند. این یعنی هیچ کدی بعد از خطِredirectاجرا نخواهد شد. پس همیشه کارهایی مثلrevalidatePathرا در خطوط قبل از آن بنویسید.
توابع سرور دسترسی کاملی به API کوکیها از طریق next/headers دارند. شما میتوانید کوکیها را بخوانید، مقداردهی کنید یا حذف کنید. نکته جذاب اینجاست که به محض تغییر یا حذف یک کوکی، Next.js به صورت خودکار صفحه فعلی و لایوتهای آن را روی سرور دوباره رندر میکند تا UI سریعاً بر اساس دیتای جدید کوکی بهروزرسانی شود.
// app/actions.ts
'use server'
import { cookies } from 'next/headers'
export async function exampleAction() {
const cookieStore = await cookies()
// دریافت کوکی
const userTheme = cookieStore.get('theme')?.value
// تنظیم کوکی جدید
cookieStore.set('theme', 'dark')
// حذف کوکی
cookieStore.delete('theme')
}
🧠 حفظ استیت کلاینت: این رندر مجدد فقط روی ساختار سرور (React Tree) اعمال میشود؛ کامپوننتهای کلاینت دوباره سوار (Mount/Unmount) میشوند اما استیتهای محلی کلاینت (
useState) کاملاً حفظ خواهند شد و آسیب نمیبینند.
useEffectهمیشه لازم نیست کاربر روی دکمهای کلیک کند؛ گاهی نیاز است به محض لود شدن کامپوننت (Mount)، یک تابع سرور به صورت خودکار اجرا شود (مثلاً برای ثبت تعداد بازدیدهای یک مقاله، سیستم اسکرول بینهایت یا میانبرهای کیبورد). در این حالت، ترکیب useEffect و useTransition بهترین الگو است:
// app/view-count.tsx
'use client'
import { incrementViews } from './actions'
import { useState, useEffect, useTransition } from 'react'
export default function ViewCount({ initialViews }: { initialViews: number }) {
const [views, setViews] = useState(initialViews)
const [isPending, startTransition] = useTransition()
useEffect(() => {
// اجرای تابع سرور داخل یکTransition برای مدیریت ناهمگام استیت
startTransition(async () => {
const updatedViews = await incrementViews()
setViews(updatedViews)
})
}, []) // آرایه وابستگی خالی یعنی فقط یکبار در زمان لود صفحه اجرا شود
return (
<div>
<p>تعداد بازدید کل: {views}</p>
{isPending && <span>در حال بهروزرسانی آمار...</span>}
</div>
)
}
هوک useActionState وضعیت لودینگ (pending) توابع سرور را به سادهترین شکل مدیریت میکند.
متد refresh() ساختار روتر کلاینت را بدون دست زدن به دیتای تگشده تازه میکند، در حالی که revalidatePath() رسماً کشِ سمت سرورِ آن مسیر را منقضی میکند.
دستور redirect() باید همیشه آخرین خط منطق شما باشد، چون تمام کدهای بعد از خود را مسدود میکند.
دستکاری کوکیها در Server Actions باعث بازسازی خودکار لایوتها در سمت سرور میشود، بدون اینکه استیتهای کلاینت ریآکت از بین بروند.
با استفاده از useEffect همراه با useTransition میتوان اکشنهای سرور را به صورت خودکار در زمان رندر اولیه اجرا کرد.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://nextjs.org/docs/app