مدیریت Submit در فرم‌ها

در درس‌های قبلی این فصل، تک‌تک بخش‌های یک فرم را بررسی کردیم: عناصر ورودی، مدیریت State و اعتبارسنجی. در این درس، به‌طور مشخص به لحظه‌ای می‌پردازیم که همه این بخش‌ها به هم می‌رسند: ارسال فرم (Submit).

چرا از رویداد onSubmit فرم استفاده می‌کنیم، نه onClick دکمه

یک اشتباه نسبتاً رایج، مدیریت ارسال فرم از طریق رویداد onClick روی دکمه ارسال است، به‌جای استفاده از onSubmit روی خود تگ form. این روش مشکلاتی دارد؛ برای مثال، اگر کاربر داخل یکی از فیلدهای فرم، کلید Enter را بفشارد، مرورگر به‌طور پیش‌فرض فرم را Submit می‌کند، حتی بدون کلیک روی دکمه. اگر منطق ارسال فقط روی onClick دکمه نوشته شده باشد، این حالت به‌درستی مدیریت نمی‌شود.

function LoginForm() {
  function handleSubmit(e) {
    e.preventDefault();
    console.log("فرم ارسال شد");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" />
      <button type="submit">ورود</button>
    </form>
  );
}

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

اهمیت e.preventDefault در Submit

رفتار پیش‌فرض مرورگر هنگام Submit شدن یک فرم HTML، ارسال درخواست به سرور و بارگذاری مجدد کامل صفحه است. در برنامه‌های React، معمولاً می‌خواهیم این فرایند را خودمان با جاوااسکریپت (مثلاً ارسال درخواست با fetch، بدون رفرش صفحه) مدیریت کنیم. به همین دلیل، تقریباً همیشه در ابتدای تابع مدیریت‌کننده Submit، e.preventDefault() فراخوانی می‌شود تا از این رفتار پیش‌فرض جلوگیری شود.

ترکیب Submit با اعتبارسنجی

معمولاً پیش از انجام کار اصلی (مانند ارسال داده)، ابتدا باید بررسی کنیم آیا داده‌های فرم معتبر هستند یا نه. همان تابع اعتبارسنجی که در درس قبل دیدیم، می‌تواند داخل Handler مربوط به Submit فراخوانی شود:

function SignupForm() {
  const [formData, setFormData] = useState({ email: "", password: "" });
  const [errors, setErrors] = useState({});

  function validate() {
    const newErrors = {};
    if (!formData.email.includes("@")) {
      newErrors.email = "ایمیل معتبر نیست";
    }
    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  }

  function handleSubmit(e) {
    e.preventDefault();

    if (!validate()) {
      return; // اگر معتبر نبود، ادامه فرایند متوقف می‌شود
    }

    console.log("ارسال داده:", formData);
  }

  return <form onSubmit={handleSubmit}>{/* عناصر فرم */}</form>;
}

وضعیت در حال ارسال (Submitting State)

وقتی ارسال فرم شامل یک عملیات غیرهمزمان است (مثلاً ارسال درخواست به سرور)، معمولاً مفید است یک State جداگانه برای نمایش وضعیت «در حال ارسال» نگه داریم؛ هم برای نمایش بازخورد به کاربر (مثلاً غیرفعال کردن دکمه) و هم برای جلوگیری از ارسال چندباره:

function SignupForm() {
  const [isSubmitting, setIsSubmitting] = useState(false);

  async function handleSubmit(e) {
    e.preventDefault();
    setIsSubmitting(true);

    // فرض: ارسال داده به سرور
    await new Promise((resolve) => setTimeout(resolve, 1000));

    setIsSubmitting(false);
  }

  return (
    <form onSubmit={handleSubmit}>
      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? "در حال ارسال..." : "ارسال"}
      </button>
    </form>
  );
}

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

مثال قابل اجرا

مدیریت کامل Submit همراه با اعتبارسنجی
برای دیدن پیش‌نمایش کامپوننت، روی Run بزنید.

جمع‌بندی

مدیریت درست Submit یعنی استفاده از رویداد onSubmit روی خود تگ form (نه onClick دکمه)، فراخوانی e.preventDefault() برای جلوگیری از رفتار پیش‌فرض مرورگر، اجرای اعتبارسنجی پیش از ادامه کار، و در صورت نیاز، نگه‌داشتن یک وضعیت جداگانه برای در حال ارسال بودن فرم. در درس بعدی به الگوهای رایج دیگری در مدیریت فرم‌ها می‌پردازیم.