Menu

טפסים ב-React: שדות קלט, checkbox, select וולידציה

בונים טפסים ב-React עם שדות מבוקרים: שדות טקסט, textarea, select, checkboxes וכפתורי רדיו, אובייקט state אחד לשדות רבים, שליחה עם onSubmit, הצגת שגיאות ולידציה, ו-form actions של React 19.

בדף הזה יש עורכים שאפשר להריץ - לערוך, להריץ ולראות את הפלט מיד.

טופס React מחזיק את הערך של כל שדה ב-state: השדה מציג value={state} ומעדכן אותו עם onChange. כשהמשתמש שולח, handler של onSubmit קורא ל-e.preventDefault() כדי לעצור את רענון הדף, ואז משתמש בערכים שכבר נמצאים ב-state.

הקלידו שם ולחצו Enter או על הכפתור. אחר כך מחקו את השורה e.preventDefault();: התצוגה המקדימה מציגה את השדות שהדפדפן היה שולח בשליחה נייטיב, שבדף אמיתי גם הייתה מרעננת אותו.

שדה שהערך שלו מגיע מ-state נקרא שדה מבוקר (controlled input). החלופה, לתת ל-DOM להחזיק את הערך ולקרוא אותו אחר כך, מכוסה ב-קומפוננטות מבוקרות מול לא מבוקרות.

שדות טקסט ו-textarea

כל שדה טקסט עובד אותו דבר: value מ-state, ו-onChange קורא את e.target.value. <textarea> עוקב אחרי אותו דפוס. ב-HTML הטקסט שלו נכתב בין התגיות, אבל ב-React מעבירים אותו כ-value.

מכיוון שאתם קובעים את הערך בעצמכם, אפשר לשנות את מה שהמשתמש הקליד לפני שהוא מוצג: שדה הכותרת הופך כל אות לאות גדולה. מונה התווים מתעדכן בכל הקשה כי body הוא state וכל שינוי מרנדר שוב.

select

<select> מקבל value על אלמנט ה-select עצמו, לא selected על option. e.target.value הוא ה-value של האפשרות שנבחרה.

ל-select עם בחירה מרובה, הוסיפו multiple והעבירו מערך בתור value. קראו את הבחירות עם Array.from(e.target.selectedOptions, (o) => o.value).

checkbox וכפתורי רדיו

checkbox נשלט עם checked, לא value, וה-handler שלו קורא את e.target.checked, שהוא true או false. כפתורי רדיו בקבוצה חולקים name; כל אחד מסומן כשה-state שווה לערך שלו.

קריאה של e.target.value ב-checkbox היא טעות נפוצה: ב-checkbox בלי מאפיין value היא מחזירה את המחרוזת "on" בין אם התיבה מסומנת ובין אם לא.

אובייקט state אחד לשדות רבים

טופס עם עשרה שדות לא צריך עשר קריאות useState ועשרה handlers. החזיקו אובייקט אחד, תנו לכל שדה name שתואם למפתח, וכתבו handler אחד שמשתמש בשם כמפתח מחושב: [e.target.name].

ה-handler בודק את סוג השדה כך שה-checkbox שומר בוליאני והאחרים שומרים טקסט. מוסיפים שדה על ידי הוספת מפתח לאובייקט ההתחלתי ושדה עם אותו name; ה-handler לא צריך שינוי. ה-spread שומר על כל שאר השדות (ראו עדכון מערכים ואובייקטים).

שליחת טופס

שימו את לוגיקת השליחה ב-onSubmit של הטופס, לא ב-onClick של הכפתור. onSubmit מופעל גם כשהמשתמש לוחץ Enter בשדה טקסט, מה ש-handler של לחיצה היה מפספס. <button> בתוך טופס הוא כפתור שליחה כברירת מחדל; תנו לכפתורים אחרים type="button" כדי שלא ישלחו.

פתחו את הקונסול, הקלידו סיסמה ולחצו Enter: הטופס רושם את כל האובייקט. לחיצה על "Clear" לא רושמת כלום, כי הוא type="button". הסירו את המאפיין הזה וגם Clear יתחיל לשלוח.

ולידציה והצגת שגיאות

ולידציה פשוטה היא פונקציה שמקבלת את הערכים ומחזירה אובייקט של הודעות. הריצו אותה בשליחה, שמרו את התוצאה ב-state, והציגו כל הודעה מתחת לשדה שלה. השביתו את כפתור השליחה בזמן שבקשה רצה, כדי שלחיצה כפולה לא תשלח את הטופס פעמיים.

לחצו על Save כשהשדות ריקים כדי לראות את שתי ההודעות, ואז מלאו אותם. saveProfile מחליפה בקשה אמיתית: היא מחכה 800 מילישניות ומסתיימת בהצלחה. המאפיין noValidate מכבה את בועות הוולידציה של הדפדפן עצמו כך שרק ההודעות שלכם מוצגות; הסירו אותו והוסיפו required לשדה כדי להשוות.

אפשר גם לחשב שגיאות בזמן הרינדור במקום לשמור אותן, למשל const emailError = form.email && !form.email.includes('@'), שמציג את ההודעה תוך כדי הקלדה. בחרו את התזמון שמתאים לטופס: בשליחה זה רגוע יותר, ותוך כדי הקלדה נותן משוב מהיר יותר.

form actions ב-React 19

React 19 מוסיפה דרך נוספת לטפל בטופס. מעבירים פונקציה ל-prop action של הטופס. React מונעת בשבילכם את רענון הדף וקוראת לפונקציה עם אובייקט FormData שמחזיק כל שדה שיש לו name. השדות לא צריכים state בכלל: הם לא מבוקרים, ו-React מנקה אותם אחרי שה-action מסתיים.

הקלידו תגובה משלכם ופרסמו אותה. הרשימה מציגה את הטקסט שלכם, והשדה חוזר ל-Nice docs: אחרי שה-action מסתיים, React מאפסת שדות לא מבוקרים ל-defaultValue שלהם. ה-action יכול להיות async, ובזמן שהוא רץ React מתייחסת לעדכון כאל transition.

כדי להציג מצב pending או שגיאה מה-action, עטפו אותו ב-useActionState, שמחזיר את התוצאה האחרונה, action עטוף בשביל הטופס, ודגל isPending. כפתור שנמצא עמוק בתוך הטופס יכול גם לקרוא את מצב ה-pending עם useFormStatus מ-react-dom:

import { useFormStatus } from 'react-dom';

function SubmitButton() {
    const { pending } = useFormStatus();
    return <button disabled={pending}>{pending ? 'Posting...' : 'Post'}</button>;
}

useFormStatus לא נטען בעורכים כאן, ולכן הוא מופיע רק בבלוק הסטטי הזה. צריך לקרוא לו מקומפוננטה שמרונדרת בתוך ה-<form>, לא בקומפוננטה שמרנדרת את הטופס.

state מבוקר עם onSubmit ו-form actions עם FormData עובדים שניהם ב-React 19. שדות מבוקרים מתאימים לטפסים שמגיבים תוך כדי הקלדה (ולידציה חיה, מונה תווים, שדות שתלויים זה בזה). actions מתאימים לטפסים שבהם צריך את הערכים רק כשהמשתמש שולח.

שאלות נפוצות

איך מקבלים את הערך של שדה קלט ב-React?

שומרים אותו ב-state ומעדכנים אותו מ-onChange: <input value={name} onChange={e => setName(e.target.value)} />. הטקסט הנוכחי תמיד נמצא אז ב-name. ב-checkboxes קראו את e.target.checked במקום e.target.value.

איך מונעים מטופס לרענן את הדף ב-React?

קראו ל-e.preventDefault() בתחילת ה-handler של onSubmit. בלי זה הדפדפן מבצע שליחת טופס רגילה, שעוברת לדף אחר וטוענת אותו מחדש.

איך מטפלים בהרבה שדות עם state אחד ב-React?

מחזיקים אובייקט אחד ב-state, נותנים לכל שדה name שתואם למפתח, ומשתמשים ב-handler אחד עם מפתח מחושב: setForm({ ...form, [e.target.name]: e.target.value }).

למה textarea משתמש ב-value ולא ב-children ב-React?

ב-HTML הטקסט של textarea יושב בין התגיות שלו, אבל ב-React מעבירים אותו כ-value (או defaultValue), בדיוק כמו בשדה input. כך כל שדה טקסט בטופס React עובד אותו דבר.

מה הם form actions ב-React 19?

ב-React 19 אפשר להעביר פונקציה ל-<form action={fn}>. React מונעת את רענון הדף, קוראת לפונקציה עם ה-FormData של הטופס, ומאפסת שדות לא מבוקרים כשהיא מסיימת. useActionState מוסיף מעל זה דגל pending ותוצאה.

איור של שפות התכנות ב-Coddy

ללמוד תכנות עם Coddy

להתחיל