نحوه ارسال آرگومان اضافی به سرور اکشن در Next.js

سرور اکشن در Next.js به ما این امکان را می‌دهد تا data mutation و مدیریت داده‌های asynchronous را به شکلی مؤثر انجام دهیم. گاهی لازم است یک تابع asynchronous مستقل روی سرور اجرا کنیم تا کارهایی مثل ذخیره داده در دیتابیس، ارسال ایمیل، دانلود PDF، پردازش تصاویر و غیره را انجام دهیم.

Next.js

Server Actions
Server Actions را ارائه می‌دهد که توابع asynchronous هستند و روی سرور اجرا می‌شوند. ما می‌توانیم از سرور اکشن‌ها برای data mutationها در سرور استفاده کنیم، اما این اکشن‌ها می‌توانند هم از سرور کامپوننت‌ها و هم از کلاینت کامپوننت‌ها فراخوانی شوند.

استفاده از سرور اکشن یک راه حل عالی برای مدیریت ارسال فرم‌ها در Next.js هستند، چرا که اکشن زمانی اجرا می‌شود که داده‌های فرم ارسال شوند. در این مقاله قصد داریم تا به بررسی یک کاربرد عملی از مدیریت آرگومان‌های اضافی در سرور اکشن Next.js بپردازیم.

چرا به ارسال آرگومان‌های اضافی نیاز داریم؟

هنگامی که یک سرور اکشن روی ارسال فرم اجرا می‌شود، داده‌های فرم به صورت خودکار به سرور اکشن ارسال می‌گردند. به‌عنوان مثال، فرم زیر را در نظر می‌گیریم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<form className="p-4 flex" action={updateUser}>
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
<form className="p-4 flex" action={updateUser}> <Input className="w-1/2 mx-2" type="text" name="name" /> <Button type="submit">Update User Name</Button> </form>
<form className="p-4 flex" action={updateUser}>
  <Input className="w-1/2 mx-2" type="text" name="name" />
  <Button type="submit">Update User Name</Button>
</form>

در این مثال، وقتی فرم ارسال می‌شود، سرور اکشنی به نام

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

همان‌طور که در قطعه کد زیر مشاهده می‌کنیم، تابع

updateUser
updateUser آرگومانی به نام
formData
formData دریافت می‌کند و می‌توانیم مقدار فیلد
name
name را از آن استخراج نماییم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"use server"
export async function updateUser(formData) {
const name = formData.get('name');
console.log(name);
}
"use server" export async function updateUser(formData) { const name = formData.get('name'); console.log(name); }
"use server"

export async function updateUser(formData) {
  const name = formData.get('name');
  console.log(name);
}

این الگو بیشتر موارد استفاده بیسیک را پوشش می‌دهد، اما گاهی باید آرگومان‌های اضافی را به صورت برنامه‌ریزی شده به سرور اکشن‌ها ارسال کنیم. این آرگومان‌ها بخشی از فرم، داده‌های فرم یا ورودی‌های کاربر نیستند؛ بلکه، مقادیری هستند که به صورت برنامه‌ریزی شده به سرور اکشن ما ارسال می‌شوند.

برای درک بهتر این موضوع، قطعه کد سرور اکشن زیر را بررسی می‌کنیم. این همان سرور اکشنی است که قبلاً دیدیم، اما در اینجا آرگومان اضافی

userId
userId همراه با آرگومان معمولی
formData
formData ارسال شده است.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"use server"
export async function updateUser(userId, formData) {
const name = formData.get('name');
console.log(userId);
console.log(name);
}
"use server" export async function updateUser(userId, formData) { const name = formData.get('name'); console.log(userId); console.log(name); }
"use server"

export async function updateUser(userId, formData) {
  const name = formData.get('name');
  console.log(userId);
  console.log(name);
}

مقدار

userId
userId چیزی داخلی و مرتبط با برنامه است، و ما قصد نداریم از کاربر بخواهیم که این مقدار را به عنوان بخشی از ارسال فرم ارائه دهد. بلکه، ممکن است نیاز باشد این مقدار را به صورت برنامه‌ریزی شده به سرور اکشن خود ارسال کنیم تا محاسبات بیشتری انجام دهیم.

حال که مورد استفاده را درک کردیم، بررسی می‌کنیم که چگونه می‌توانیم این کار را انجام دهیم. برای این کار، یک فرم و یک سرور اکشن کاربردی برای آن می‌سازیم.

ساخت فرم با سرور اکشن

ابتدا، یک پوشه به نام

actions
actions در داخل پوشه
app
app پروژه Next.js خود ایجاد می‌کنیم. سپس، یک فایل به نام
user.js
user.js داخل پوشه
actions
actions می‌سازیم و کد زیر را در آن قرار می‌دهیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"use server"
export async function updateUser(formData) {
const name = formData.get('name');
console.log(name);
// Do anything with the name, save in DB, create invoice, whatever!
}
"use server" export async function updateUser(formData) { const name = formData.get('name'); console.log(name); // Do anything with the name, save in DB, create invoice, whatever! }
"use server"

export async function updateUser(formData) {
  const name = formData.get('name');
  console.log(name);

  // Do anything with the name, save in DB, create invoice, whatever!
}

این همان روش در Next.js است که به کمک آن یک تابع سرور ایجاد می‌کنیم. باید در بالای فایل، یک دستور

”use server”
”use server” قرار دهیم تا به Next.js بگوییم که این یک فایل ویژه است که شامل یک یا چند تابع asynchronous می‌باشد که روی سرور اجرا می‌شوند.

سپس، یک سرور اکشن به نام

updateUser
updateUser داریم که آرگومانی به نام
formData
formData می‌گیرد. در داخل این تابع، مقدار فیلد
name
name استخراج شده و در کنسول چاپ می‌شود.

اکنون این سرور اکشن را به یک فرم متصل می‌کنیم. برای این کار، یک پوشه به نام

components
components در پوشه‌ی root پروژه می‌سازیم. پس از آن، یک فایل به نام
user-form.jsx
user-form.jsx ایجاد کرده و کد زیر را در آن قرار می‌دهیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Input } from "./ui/input"
import { Button } from "./ui/button"
import { updateUser } from "@/app/actions/user"
const UserForm = () => {
return(
<form className="p-4 flex" action={updateUser}>
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
)
}
export default UserForm;
import { Input } from "./ui/input" import { Button } from "./ui/button" import { updateUser } from "@/app/actions/user" const UserForm = () => { return( <form className="p-4 flex" action={updateUser}> <Input className="w-1/2 mx-2" type="text" name="name" /> <Button type="submit">Update User Name</Button> </form> ) } export default UserForm;
import { Input } from "./ui/input"
import { Button } from "./ui/button"

import { updateUser } from "@/app/actions/user"

const UserForm = () => {
  return(
    <form className="p-4 flex" action={updateUser}>
      <Input className="w-1/2 mx-2" type="text" name="name" />
      <Button type="submit">Update User Name</Button>
    </form>
  )
}

export default UserForm;

این یک کامپوننت ساده React است که شامل یک فرم می‌باشد. فرم یک فیلد متنی به نام

name
name و یک دکمه برای ارسال دارد. ویژگی
action
action فرم به سرور اکشن
updateUser
updateUser اشاره می‌کند. حالا وقتی فرم با مقدار
name
name ارسال می‌شود، همان‌طور که قبلاً گفتیم، سرور اکشن آن را به عنوان بخشی از داده‌های فرم دریافت می‌کند.

تست فرم

برای تست این فرم، یک route و یک page جدید در Next.js ایجاد می‌کنیم. ابتدا، یک پوشه به نام

extra-args
extra-args در دایرکتوری
app
app می‌سازیم. سپس، یک فایل به نام
page.js
page.js در پوشه
app/extra-args
app/extra-args ایجاد کرده و کد زیر را در آن قرار می‌دهیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import UserForm from "@/components/user-form";
const ExtraArgsDemo = () => {
return (
<UserForm />
)
}
export default ExtraArgsDemo;
import UserForm from "@/components/user-form"; const ExtraArgsDemo = () => { return ( <UserForm /> ) } export default ExtraArgsDemo;
import UserForm from "@/components/user-form";

const ExtraArgsDemo = () => {
  return (
    <UserForm />
  )
}

export default ExtraArgsDemo;

این یک کامپوننت ساده React است که در آن کامپوننت

UserForm
UserForm را import کرده و در JSX مورد استفاده قرار می‌دهیم. اکنون سرور لوکال را اجرا کرده و مسیر
localhost:3000/extra-args
localhost:3000/extra-args را در مرورگر خود باز می‌کنیم. باید فرمی با یک فیلد متنی و یک دکمه مشاهده نماییم.

تست عملکرد

یک متن در فیلد تایپ کرده و روی دکمه کلیک می‌کنیم.
اکنون می‌توانیم متن تایپ شده را در کنسول سرور ببینیم. اما چرا این متن در کنسول سرور نمایش داده می‌شود و نه در کنسول مرورگر؟ این به این دلیل است که سرور اکشن‌ها فقط روی سرور اجرا می‌شوند، نه در مرورگر کلاینت.

حال یک جریان داده‌ای به این شکل ایجاد کرده‌ایم:

Page => Form => Server Action

page شامل یک فرم است. فرم در هنگام ارسال، یک سرور اکشن را اجرا می‌کند. سرور اکشن داده‌های فرم را در کنسول سرور چاپ می‌کند.

در ادامه مقاله قصد داریم تا این بخش‌ها را بهبود دهیم تا بتوانیم آرگومان‌های اضافی را به سرور اکشن ارسال کنیم.

چگونه می‌توانیم آرگومان‌های اضافی را به سرور اکشن ارسال کنیم؟

اکنون می‌خواهیم یک prop به نام

userId
userId را از page به کامپوننت
UserForm
UserForm ارسال کنیم. این prop به عنوان یک مقدار مشخص ارسال می‌شود تا نشان دهد که چگونه می‌توانیم این
userId
userId را به صورت برنامه‌ریزی شده به فرم و سپس به سرور اکشن ارسال نماییم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import UserForm from "@/components/user-form";
const ExtraArgsDemo = () => {
return (
<UserForm userId={"1234"} />
)
}
export default ExtraArgsDemo;
import UserForm from "@/components/user-form"; const ExtraArgsDemo = () => { return ( <UserForm userId={"1234"} /> ) } export default ExtraArgsDemo;
import UserForm from "@/components/user-form";

const ExtraArgsDemo = () => {
  return (
    <UserForm userId={"1234"} />
  )
}

export default ExtraArgsDemo;

در کامپوننت

UserForm
UserForm، prop به نام
userId
userId را دریافت می‌کنیم. حالا باید یک کار ویژه انجام دهیم تا این
userId
userId به سرور اکشن
updateUser
updateUser ارسال شود.

جاوااسکریپت متدی به نام

bind()
bind() دارد که به ما این امکان را می‌دهد تا یک تابع با آرگومان‌های از پیش تنظیم شده بسازیم. این تابع، نسخه‌ای جدید از تابع اصلی است که آرگومان‌های اولیه در آن تنظیم شده‌اند.

در مثال ما، تابع

updateUser
updateUser از قبل یک آرگومان به نام
formData
formData دارد. اکنون می‌توانیم از متد
bind()
bind() استفاده کنیم تا آرگومان اضافی
userId
userId را به آن اضافه کرده و یک تابع جدید بسازیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const updatedUserWithId = updateUser.bind(null, userId);
const updatedUserWithId = updateUser.bind(null, userId);
const updatedUserWithId = updateUser.bind(null, userId);

اولین آرگومان متد

bind()
bind()، context است که تابع را به آن متصل می‌کند. این context تعیین می‌کند که مقدار
this
this در داخل تابع به چه چیزی اشاره داشته باشد. در اینجا می‌توانیم آن را
null
null قرار دهیم، چون قصد تغییر آن را نداریم. سپس، آرگومان جدید
userId
userId را ارسال می‌کنیم.

نکته‌ای که باید به آن توجه داشته باشیم این است که متد

bind()
bind() هم در سرور کامپوننت‌ها و هم در کلاینت کامپوننت‌ها قابل استفاده است.

نسخه تغییر ‌یافته کامپوننت UserForm

در این بخش نسخه تغییر یافته کامپوننت

UserForm
UserForm در فایل
user-form.jsx
user-form.jsx را مشاهده می‌کنیم، که مقدار ویژگی action فرم به تابع جدید
updatedUserWithId
updatedUserWithId تغییر پیدا کرده است.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
import { Input } from "./ui/input"
import { Button } from "./ui/button"
import { updateUser } from "@/app/actions/user"
const UserForm = ({userId}) => {
const updatedUserWithId = updateUser.bind(null, userId);
return(
<form className="p-4 flex" action={updatedUserWithId}>
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
)
}
export default UserForm;
import { Input } from "./ui/input" import { Button } from "./ui/button" import { updateUser } from "@/app/actions/user" const UserForm = ({userId}) => { const updatedUserWithId = updateUser.bind(null, userId); return( <form className="p-4 flex" action={updatedUserWithId}> <Input className="w-1/2 mx-2" type="text" name="name" /> <Button type="submit">Update User Name</Button> </form> ) } export default UserForm;
import { Input } from "./ui/input"
import { Button } from "./ui/button"

import { updateUser } from "@/app/actions/user"

const UserForm = ({userId}) => {
  const updatedUserWithId = updateUser.bind(null, userId);

  return(
    <form className="p-4 flex" action={updatedUserWithId}>
      <Input className="w-1/2 mx-2" type="text" name="name" />
      <Button type="submit">Update User Name</Button>
    </form>
  )
}

export default UserForm;

اکنون سرور اکشن مقدار

userId
userId را به عنوان آرگومان دریافت می‌کند. حال این مقدار را همراه با دیگر داده‌ها در کنسول چاپ می‌کنیم.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
"use server"
export async function updateUser(userId, formData) {
const name = formData.get('name');
console.log(userId);
console.log(name);
// Do anything with the user id and name, save in DB,
// create invoice, whatever!
}
"use server" export async function updateUser(userId, formData) { const name = formData.get('name'); console.log(userId); console.log(name); // Do anything with the user id and name, save in DB, // create invoice, whatever! }
"use server"

export async function updateUser(userId, formData) {
  const name = formData.get('name');
  console.log(userId);
  console.log(name);

  // Do anything with the user id and name, save in DB, 
  // create invoice, whatever!
}

در این مرحله، اگر فرم را با یک مقدار برای

name
name ارسال کنیم، خواهیم دید که هم مقدار
userId
userId و هم مقدار
name
name در کنسول سرور لاگ می‌شوند. در این حالت، یکی از مقادیر از داده‌های فرم گرفته می‌شود و مقدار دیگر مستقیماً به سرور اکشن ارسال می‌گردد.

تا این قسمت از مقاله یاد گرفتیم که چگونه می‌توانیم آرگومان‌های اضافی را همراه با داده‌های فرم به سرور اکشن ارسال کنیم.

نقش و کاربرد فیلدهای hidden در فرم‌ها

HTML از نوع ورودی hidden پشتیبانی می‌کند که می‌توانیم از آن برای ارسال داده‌های سمت کلاینت به سرور، بدون دریافت ورودی مستقیم از کاربران استفاده کنیم. یعنی ما می‌توانستیم مقدار

userId
userId را با استفاده از یک فیلد مخفی به این شکل ارسال کنیم:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<form className="p-4 flex" action={updatedUserWithId}>
<Input type="hidden" name="userId" type="1234" />
<Input className="w-1/2 mx-2" type="text" name="name" />
<Button type="submit">Update User Name</Button>
</form>
<form className="p-4 flex" action={updatedUserWithId}> <Input type="hidden" name="userId" type="1234" /> <Input className="w-1/2 mx-2" type="text" name="name" /> <Button type="submit">Update User Name</Button> </form>
<form className="p-4 flex" action={updatedUserWithId}>
      <Input type="hidden" name="userId"  type="1234" />
      <Input className="w-1/2 mx-2" type="text" name="name" />
      <Button type="submit">Update User Name</Button>
</form>

اما ما از متد

bind()
bind() استفاده کردیم. دلیل این اتفاق این است که فیلدهای hidden نگرانی‌های امنیتی دارند. وقتی داده‌ها را از طریق فیلد hidden ارسال می‌کنیم، HTML رندر شده شامل مقدار آن می‌شود و این مقدار به طور پیش‌فرض رمزگذاری نمی‌گردد. به همین دلیل، بهتر است داده‌ها را به صورت برنامه‌ریزی شده مدیریت کنیم تا امنیت بیشتری داشته باشند.

همچنین، از طریق این لینک می‌توانیم به فایل کامل پروژه دسترسی داشته باشیم.

جمع‌بندی

در این مقاله یاد گرفتیم که سرور اکشن در Next.js چگونه به ما این امکان را می‌دهد تا عملیات asynchronous مانند ذخیره داده‌ها، ارسال ایمیل‌ها و پردازش فرم‌ها را به راحتی روی سرور اجرا کنیم. همچنین دریافتیم که چگونه می‌توانیم علاوه بر داده‌های فرم، پارامترهای اضافی را به این اکشن‌ها ارسال کنیم.

ما در این مقاله از متد

bind()
bind() استفاده کردیم که کمک کرد داده‌های برنامه‌ریزی شده مثل
userId
userId را به صورت ایمن و بدون افشای آن‌ها در HTML ارسال کنیم. این روش نسبت به استفاده از فیلدهای hidden امن‌تر است و کنترل بیشتری روی داده‌ها به ما می‌دهد.

در نهایت، با مثال‌های ساده و عملی دیدیم که چطور می‌توانیم این قابلیت‌ها را در پروژه‌های واقعی پیاده‌سازی کرده و از قدرت سرور اکشن در Next.js بهره‌مند شویم.

دیدگاه‌ها:

افزودن دیدگاه جدید