Next.js یک فریم‌ورک مبتنی بر React برای ساخت برنامه‌های کاربردی، بهینه‌شده و مقیاس‌پذیر است. ما به‌عنوان توسعه‌دهندگان React، کامپوننت‌ها را می‌سازیم، state را manage و ارتباطات داده‌ای بین آن‌ها را مدیریت می‌کنیم. سپس Next.js مطرح می‌شود تا بهینه‌سازی‌های مورد نیاز را ارائه دهد.

Next.js 14 مفهوم پایدار App Routerرا در اختیار ما قرار می‌دهد که شامل موارد زیر می‌باشد:

در این مقاله قصد داریم تا یک تکنیک Next.js برای data fetching به نام Server Action را مورد بحث قرار دهیم. اما قبل از آن باید با مفهوم React Server Componentها آشنا شویم.

تیم ReactJS مفهوم server componentها را در سال ۲۰۲۰ معرفی کرد. مفهوم app routerدر Next.js با استفاده از طرز فکر «تفکر در server componentها» ساخته شده است.

باید به این نکته توجه کنیم که مفهوم Server Component و Server Action یکسان نیست. برای درک آسان Server Actionها، باید یک آشنایی اولیه‌ای از Server Componentها داشته باشیم.

Formها و Actionها در React

به عنوان یک توسعه‌دهنده وب، ما فرم‌های HTML و form actionها را بارها پیاده‌سازی کرده‌ایم. به عنوان مثال:

export default function Cart() {

  function addToCart(e) {
    // Read the Form Data
    // Make the API call
    // Change the Component state
  }

  return (
    <form method="post" onSubmit={addToCart}>
      <label>
        Select your favorite brand:
        <select name="selectedBrand" defaultValue="apple">
          <option value="apple">Apple</option>
          <option value="oppo">Oppo</option>
          <option value="samsung">Samsung</option>
        </select>
      </label>
      <label>
        Enter the Count:
        <input
          type="number"
          name="count"
          placeholder="Specify how many do you want" />
      </label>
      <hr />
      <button type="reset">Reset</button>
      <button type="submit">Submit</button>
    </form>
  );
}

کدی که داریم یک کامپوننت ساده React است که با استفاده از فرم، افزودن یک محصول و مقدار آن را به سبد خرید انجام می‌دهد. تابع addToCartکه داریم قرار است در حین افزودن موارد انتخاب شده به سبد خرید، وظایفی را انجام دهد که عبارتند از:

باید به این نکته توجه داشته باشیم که همه این‌ها در سمت کلاینت برنامه ما اتفاق می‌افتد. این بدان معنی است که کد کامپوننت به عنوان بخشی از بسته نرم افزاری در مرورگر کاربر دانلود می‌شود. کامپوننت یک API call از طریق شبکه برقرار می‌کند و منتظر می‌ماند تا کامل شود. این انتظار می‌تواند بر اساس تأخیر، سرعت شبکه و غیره طولانی‌تر باشد.

بررسی React Server Componentها

ما می‌توانیم کامپوننت‌های خود را با کمک React Server Components (RSC) به سرور منتقل کرده و آن‌ها را در پایگاه داده ذخیره کنیم تا:

این موارد با توجه به تجربه کاربری، قابلیت نگه‌داری و هزینه عملکرد، مزایای بسیار مهمی هستند.

وقتی کامپوننت‌ها را با React Server Componentها به سرور منتقل می‌کنیم، به احتمال زیاد در پایگاه داده برنامه ما قرار می‌گیرند. ما هنوز هم باید داده‌ها را از پایگاه داده به کامپوننت‌های خود fetch کنیم، اما fetch کردن داده‌ها بسیار سریع‌تر از fetch کردن از یک client component خواهد بود.

مفهوم app routerدر Next.js براساس «تفکر در server componentها» ساخته شده است. همه کامپوننت‌ها به‌طور پیش‌فرض، server component هستند و اگر می‌خواهیم یک client component بنویسیم، باید آن‌ها را به صراحت با استفاده از دستورالعمل use Clientعلامت‌گذاری کنیم.

این بدان معنی است که یک سلسله مراتب کامپوننت در Next.js می‌تواند server componentها و client componentها را با هم ترکیب کند. ما می‌توانیم تا حد امکان از server componentها، و هر جا که نیاز به تعامل با کاربر یا مدیریت event داشته باشیم از client componentها استفاده کنیم.

منظور از Server Action در Next.js چیست؟

Next.js تکنیک‌ها و الگوهای fetching داده‌ها را برای توسعه‌دهندگان ارائه می‌دهد که یکی از این تکنیک‌ها، استفاده از server action است.

server actionها بر روی React Actions ساخته شده‌اند که می‌توانیم آن‌ها را در server componentها تعریف کرده و یا از client componentها فراخوانی کنیم. به طور کلی، server actionها توابع async جاوااسکریپت هستند که توسط تعاملات کاربر روی کلاینت، بر روی سرور اجرا می‌شوند.

async function addItemToCart(data) {
   'use server'
   await addToDB(data)
}

می‌توانیم server actionها را با استفاده از prop action المنت <form/>در ReactJS فراخوانی کنیم.

<form action={addItemToCart}>
...
...
</form>

همچنین، می‌توانیم از prop formActionروی المنت‌هایی مانند دکمه ارسال، متن ورودی و غیره در داخل یک <form/>استفاده نماییم.

<form action={handleSubmit}>
    <input type="text" name="name" formAction={handleName} />
    <button type="submit">Submit</button>
</form>

اکنون می‌خواهیم کاربرد server actionها را با توجه به server componentها و client componentها بررسی کنیم.

نحوه استفاده از server actionها در server componentهای Next.js

برای استفاده از server actionها در داخل server component، ابتدا یک تابع async با دستور use serverایجاد می‌کنیم. ما باید این دستور را در بالای بدنه تابع قرار دهیم.

سپس، می‌توانیم server function را از prop action المنت <form/> فراخوانی کنیم و یا این که از prop formAction روی المنت‌هایی مانند دکمه ارسال، متن ورودی و غیره در داخل المنت <form/> استفاده نماییم.

export default function CourseComments() {
  async function addCommentToCourse(comment, courseId) {
    'use server'

     await saveComment({comment, courseId});
  }

  return(
     <form action={addCommentToCourse}>
       <button type="submit">Add Comment</button>
     </form>
  )
}

نحوه استفاده از server actionها در client componentهای Next.js

از آنجایی که سلسله مراتب کامپوننت می‌تواند دارای server componentها و client componentها باشد، ممکن است بخواهیم از server actionها در داخل client componentها نیز استفاده کنیم. برای آن، actionهای خود را در یک فایل جداگانه، مثلا add-comments.js ایجاد می‌نماییم.

'use server'

import { addCommentToCourse } from '@/data/course'

export async function addComment(data, courseId) {
  const response = await addCommentToCourse(data, course);
  return response;
}

باید به این نکته توجه داشته باشیم که هنوز باید از دستور use serverدر بالای فایل (حتی قبل از importها) استفاده کنیم تا Next.js را در مورد فایلی که شامل server actionها است، مطلع نماییم.

اکنون کاری که باید در client component انجام دهیم این است، همانظور که قبلا دیده‌ایم، فقط actionها را import کرده آن‌ها از prop action یا prop formAction فراخوانی می‌کنیم.

'use client'

import { addComment } from '@/actions/add-comment';

export default function CourseComment() {
  return (
    <form action={addComment}>
      <button type="submit">Add Comment</button>
    </form>
  )
}

فراخوانی server actionها خارج از Formها

تا این قسمت مقاله، ما در مورد دو روش فراخوانی برای فراخوانی server actionها صحبت کردیم:

هر دوی این روش‌ها به المنت <form/>مربوط می‌شوند. اکنون سوالی که با آن مواجه هستیم این است که چگونه می‌توانیم server actionها را خارج از المنت <form/> فراخوانی کنیم؟ برای انجام این کار، می‌توانیم با استفاده از روش startTransitionارائه شده توسط هوک useTransition، فراخوانی سفارشی انجام دهیم.

بیایید فرض کنیم مانند قبل server action خود را در یک فایل جداگانه داریم و می‌خواهیم آن را از یک client component بدون المنت <form/> فراخوانی کنیم. برای انجام آن:

'use client'

import { useTransition } from 'react'
import { addComment } from '@/actions/add-comment';

export default function CourseComment() {
  let [isPending, startTransition] = useTransition()

  return (
    <button onClick={() => startTransition((comment, courseId) => { 
           addComment(comment, courseId)})}>
      Add Comment
    </button>
  )
}

جمع‌بندی

در این مقاله سعی کردیم مفهوم server action را در Next.js بررسی کنیم. همچنین مطالعه مستندات مربوط به server action در وبسایت Next.js هم می‌تواند بسیار مفید باشد.