مدیریت State فرم و اعتبارسنجی (Validation)

تا اینجا یاد گرفتیم چگونه تک‌تک عناصر فرم را به‌صورت Controlled مدیریت کنیم. اما یک فرم واقعی معمولاً چند فیلد دارد که باید با هم مدیریت شوند، و در بیشتر موارد باید قبل از ارسال، درستی مقادیر وارد‌شده را هم بررسی کرد. این درس به این دو موضوع می‌پردازد: سازمان‌دهی State چند فیلدی، و اعتبارسنجی.

مشکل: یک useState جداگانه برای هر فیلد

یک رویکرد ساده برای فرم‌های چندفیلدی، تعریف یک useState جداگانه برای هر فیلد است:

function SignupForm() {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  // ...
}

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

راه‌حل رایج: یک State واحد به شکل Object

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

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

  function handleChange(e) {
    const { name, value } = e.target;
    setFormData((prev) => ({ ...prev, [name]: value }));
  }

  return (
    <form>
      <input name="name" value={formData.name} onChange={handleChange} />
      <input name="email" value={formData.email} onChange={handleChange} />
    </form>
  );
}

در این کد، یک تابع handleChange واحد برای همه فیلدها استفاده می‌شود. با کمک ویژگی name روی هر input، مشخص می‌شود کدام فیلد از آبجکت formData باید به‌روزرسانی شود؛ به همین دلیل نیازی نیست برای هر فیلد یک تابع جداگانه نوشته شود. همچنین، همان‌طور که در فصل سوم درباره Immutability دیدیم، به‌روزرسانی این آبجکت هم با ساخت یک آبجکت جدید (با عملگر Spread) انجام می‌شود، نه با تغییر مستقیم.

اعتبارسنجی: بررسی درستی مقادیر

اعتبارسنجی یعنی بررسی این‌که آیا مقادیر وارد‌شده توسط کاربر، معیارهای موردنیاز را رعایت می‌کنند یا نه؛ مثلاً خالی نبودن یک فیلد، یا معتبر بودن فرمت ایمیل. یک الگوی رایج، نگه‌داشتن پیام‌های خطا در یک State جداگانه است:

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

  function validate() {
    const newErrors = {};

    if (!formData.email.includes("@")) {
      newErrors.email = "ایمیل معتبر نیست";
    }

    if (formData.password.length < 6) {
      newErrors.password = "رمز عبور باید حداقل ۶ کاراکتر باشد";
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  }

  function handleSubmit(e) {
    e.preventDefault();
    if (validate()) {
      console.log("فرم معتبر است، ارسال انجام شود");
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* عناصر فرم */}
      {errors.email && <p>{errors.email}</p>}
    </form>
  );
}

اعتبارسنجی لحظه‌ای در برابر اعتبارسنجی در لحظه ارسال

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

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

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

جمع‌بندی

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