اینپوتهایی مانند دکمههای رادیویی (radio button)، چکباکسها و text area ها مثالهایی از ابزارهای تعاملی میان کاربران و توسعهدهندگان اند. ما به عنوان توسعهدهندگان این موارد را به زیبایی در صفحهی وب قرار میدهیم و کاربران نیز با پر کردن و ارسال آنها، بخش مربوط به خود را انجام میدهند. مدیریت فرم ها بخش جدایی ناپذیر هر پروژهی وب اپی است و یکی از مواردیست که React نیز در آن عملکرد خوبی دارد. شما آزادی فراوانی در پیادهسازی و کنترل بر روی ورودیها یا همان اینپوتهای پر شده توسط کاربر خواهید داشت و ممکن است با روشهای مختلفی به هدف خود برسید؛ اما پرسشی که باقیست این است که بهترین شیوه برای انجام این کار چیست؟
در این مقاله قصد بررسی روشهای مختلف و متنوع مدیریت فرم ها در React را داریم؛ از استفاده از useState و هوکهای سفارشی تا حتی استفاده نکردن از استیت!
به خاطر داشتهباشید که در تمامی مثالهایمان، قصد ساخت یک فرم ورود با استفاده از ایمیل و رمز را خواهیم داشت؛ ولی اکثر تکنیکهایی که استفاده میکنیم، قابل پیادهسازی در اکثر فرم ها هستند.
لطفا همیشه accessibility را در نظر داشته باشید
اگر چه که این مورد خیلی به موضوع این مقاله مربوط نیست، اما بد نیست که همیشه فرم هایمان راaccessible نگه داریم. برای انجام این کار نیز کافیست که به اینپوتها لیبل اضافه کنیم و aria-tag های مناسبی را در هنگام invalid یا نامعتبر بودن فرم ها ست کنیم و همچنین ساختار فرم را نیز از نظر معنایی درست قرار دهیم. این موارد همگی میتوانند به استفاده هرچه آسانتر فرم توسط همهی اشخاص منجر شود؛ از جمله افراد کم توانی که برای استفاده از وبسایتها از ابزارهای کمکی مانند صفحه خوانها (screen reader) ها استفاده میکنند.
مدیریت فرم ها با استفاده از هوک useState
برای شروع بیایید ببینیم که من چگونه فرم هایم را مدیریت میکنم. من برای هر قسمت فرم استیتی مجزا در نظر میگیرم و هر کدام را به تنهایی به شکل زیر، تغییر میدهم:
function LoginForm() {
const [email, setEmail] = React.useState("");
const [password, setPassword] = React.useState("");
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
api.login(email, password);
}
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
</div>
</form>
);
}
در ابتدا دو استیت جداگانه برای ایمیل و رمز میسازیم؛ سپس آنها را به اینپوت مربطهشان پاس میدهیم و مقدارشان را برابر با مقدار اینپوتشان در نظر میگیریم. در واقع با تغییر مقدار اینپوت مقدار استیت مربوط به آن نیز بلافاصله تغییر خواهد کرد که باعث رندر مجدد اپ میشود.
این روش ساده برای بیشتر موارد کابرد دارد و پیچیدگی زیادی ندارد؛ ولی ممکن است شما از تکرار این روند در هر فرمتان خسته شوید.
ساخت هوک کاستوم
بیایید کدمان را با استفاده از یک هوک سفارشی بازسازی کنیم و روند کاری را بهبود بخشیم:
const useFormField = (initialValue: string = "") => {
const [value, setValue] = React.useState(initialValue);
const onChange = React.useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value),
[]
);
return { value, onChange };
};
export function LoginForm() {
const emailField = useFormField();
const passwordField = useFormField();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
api.login(emailField.value, passwordField.value);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
{...emailField}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
{...passwordField}
/>
</div>
</form>
);
}
ما یک هوک سفارشی با نام useFormField ساختهایم که مقدار اینپوت ها را درون استیت نگه داری میکند. این روش بر روی تمامی فیلدهای اینپوت موثر خواهد بود.
مدیریت چندین فیلد
یکی از نقاط منفی این رویکرد عدم مناسب بودن برای فرم های بزرگ است. احتمالا برای یک فرم لاگین یا ورود مناسب است اما وقتی میخواهید فرم های بزرگی بسازید، احتمالا با فیلدهای اینپوت فراوانی نیز روبرو خواهید بود. آیا در آن مورد نیز میتوان بارها و بارها هوک سفارشی را مورد استفاده قرار داد؟
در اینجا به این نتیجه رسیدهام که بهتر است یک هوک سفارشی که تمامی استیتهای فرم را درون خود نگه میدارد، بسازم. چیزی شبیه به کدی که در زیر مشاهده میکنید:
function useFormFields<T>(initialValues: T) {
const [formFields, setFormFields] = React.useState<T>(initialValues);
const createChangeHandler = (key: keyof T) => (
e: React.ChangeEvent<HTMLInputElement>,
) => {
const value = e.target.value;
setFormFields((prev: T) => ({ ...prev, [key]: value }));
};
return { formFields, createChangeHandler };
}
export function LoginForm() {
const { formFields, createChangeHandler } = useFormFields({
email: "",
password: "",
});
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
api.login(formFields.email, formFields.password);
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
value={formFields.email}
onChange={createChangeHandler("email")}
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
value={formFields.password}
onChange={createChangeHandler("password")}
/>
</div>
</form>
);
}
با استفاده از هوک useFormFields میتوانیم بدون اضافه کردن پیچیدگی به کدهایمان، به اضافه کردن فیلدهای جدید به آن بپردازیم. ما میتوانیم به تمامی استیتهای فرم در یک جا دسترسی داشته باشیم.
یک روش جایگزین
مدیریت استیت به شکل صریح به خوبی به کارمان خواهد آمد و روش پیشنهادی ریاکت نیز برای اکثر موارد، همین است. اما باید بدانید که روشی دیگر نیز وجود دارد. مرورگر به طور پیشفرض در درون خود، استیت فرم ها را مدیریت میکند و ما نیز میتوانیم از آن برای سادگی کد خود بهره ببریم.
در زیر همان کد قبلی را مشاهده میکنید؛ با این تفاوت که این بار مرورگر استیت را مدیریت میکند:
export function LoginForm() {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData(e.target as HTMLFormElement);
api.login(formData.get('email'), formData.get('password'));
};
return (
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="email">Email</label>
<input
type="email"
id="email"
name="email"
/>
</div>
<div>
<label htmlFor="password">Password</label>
<input
type="password"
id="password"
name="password"
/>
</div>
<button>Log in</button>
</form>
);
}
همان گونه که در بالا میبینید، با روشی خوب و ساده که به درستی نیز کار میکند مواجه هستیم.
احتمالا متوجه شدهاید که بخشی را در فانکشن handleSubmit کمی متفاوتتر انجام دادهایم. در این روش جدید از یک api درونی مرورگر با نام FormData استفاده کردهایم. این روشی مفید برای گرفتن مقادیر فیلدهای اینپوت است.
در این روش شما نیازی به مدیریت استیتها به طور مستقیم و صریح ندارید. علاوه بر این react دارای یک پراپ (prop) کاربردی با نام defaultValue نیز است که به شما در این روش کمک میکند.
هر روش مناسب چه کاربردی است؟
از آنجایی که فرم ها بخش جدایی ناپذیر اغلب وب اپها هستند، باید بدانیم که به چه شکلی باید آن ها را مدیریت کنیم؛ مخصوصا که ریاکت راههای زیادی را برای انجام این کار در اختیار شما میگذارد.
برای فرم های ساده نیازی به اعتبارسنجیهای سنگین نیست و من پیشنهاد استفاده از مدیریت کننده درونی استیت که به شکل پیشفرض در اختیار است را میدهم. البته که در این روش تسلط حداکثری بر رو اینپوت ها و ایجاد تغییرات در آنها را ندارید و ممکن است برای دستیابی به این اهداف از راههای دیگری که معرفی کردیم، استفاده کنید.
هنگامی که قصد انجام اعتبارسنجیهای کاستوم بر روی دادههای درون فرم هایتان را دارید، بهتر است با استفاده از کامپوننتهای کنترل شده پروژه خود را انجام دهید. پس میتوان در این مواقع از هوک useState استفاده کرد یا از روش ساخت هوک سفارشی بهره برد.
خود react نیز پیشنهاد استفاده از کامپوننتهای کنترل شده را برای بیشتر مواقع میدهد. اما باید این نکته را مد نظر داشته باشیم که در همهی موارد نیاز نیست که سادگی را قربانی کنیم.
از هرچه برای مدیریت فرم های ریاکتی استفاده میکنید بدانید که امروزه react این کار را بسیار آسان کرده است. شما میتوانید مدیریت فرم های ساده را بر عهدهی مرورگر بگذارید یا در فرم های پیچیدهتر، خودتان صراحتا استیت را مدیریت کنید. در هر صورت مطمئن باشید که تعداد خطهای کد در تمامی این موارد بسیار کمتر از گذشته شدهاست.
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید