ניהול טפסים בריאקט עם Formik ו Yup

12/01/2025

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

1. קוד הוולידציה

אני מתקין את שתי הספריות עם:

npm install formik yup

ואז יוצר תיקייה חדשה בשם schema ובתוכה קובץ לוולידציית טופס ראשונה, אני קורא לו LoginForm.ts. זה תוכן הקובץ:

import * as Yup from 'yup';

export default Yup.object().shape({
  email: Yup.string()
    .email('Invalid email format')
    .required('Email is required'),
  password: Yup.string()
    .required('Password is required'),
  rememberMe: Yup.boolean().default(false),
});

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

2. קוד הטופס

בצד של הטופס יש ממשק מאוד פשוט בין פורמיק ל yup וזה נראה כך:

import { useFormik } from 'formik';
import loginFormSchema from './schema/LoginForm';

export default () => {
  const formik = useFormik({
    initialValues: {email: '', password: '', rememberMe: false},
    validationSchema: loginFormSchema,
    onSubmit: values => {
      alert(JSON.stringify(values, null, 2));
    },
  });

  return (
    <form onSubmit={formik.handleSubmit}>
      <div>
        <label>
          Email
          <input          
            name="email"
            type="email"
            onChange={formik.handleChange}
            onBlur={formik.handleBlur}
            value={formik.values.email}
          />
          {formik.touched.email && formik.errors.email ? (
            <div>{formik.errors.email}</div>
          ) : null}    
        </label>    
    </div>

    <div>
      <label>
        Password:
        <input        
        name="password"
        type="text"
        onChange={formik.handleChange}
        onBlur={formik.handleBlur}
        value={formik.values.password}
        />
        {formik.touched.password && formik.errors.password ? (
          <div>{formik.errors.password}</div>
        ) : null}  
      </label>    
    </div>

    <div>    
      <label>
        <input
          type="checkbox"
          name="rememberMe"
          onChange={formik.handleChange}
          onBlur={formik.handleBlur}
          checked={formik.values.rememberMe}
          />
        Remember Me
      </label>
    </div>

    <button type="submit">Login</button>
    </form>
  );
};

הפקודה שמחברת בין הטופס לסכימה היא:

validationSchema: loginFormSchema,

והבלוק הזה אחרי כל input אחראי על הצגת הודעות השגיאה ליד השדה:

{formik.touched.email && formik.errors.email ? (
  <div>{formik.errors.email}</div>
) : null}