العمل مع النماذج في React

يحتاج كل تطبيق تقريبًا إلى قبول إدخال المستخدم في مرحلة ما ، ويتم تحقيق ذلك عادةً من خلال نموذج HTML الموقر ومجموعة عناصر التحكم في الإدخال. إذا كنت قد بدأت مؤخرًا في تعلم React ، فمن المحتمل أنك وصلت إلى النقطة التي تفكر فيها الآن ، “إذن كيف أعمل مع النماذج؟”

ستوجهك هذه المقالة إلى أساسيات استخدام النماذج في React للسماح للمستخدمين بإضافة المعلومات أو تحريرها. سننظر في طريقتين مختلفتين للعمل باستخدام عناصر التحكم في الإدخال وإيجابيات وسلبيات كل منها. سنلقي أيضًا نظرة على كيفية التعامل مع التحقق من الصحة وبعض مكتبات الجهات الخارجية لحالات استخدام أكثر تقدمًا.

المدخلات غير المنضبط

الطريقة الأساسية للعمل مع النماذج في React هي استخدام ما يُشار إليه بمدخلات النموذج “غير المتحكم فيها”. ما يعنيه هذا هو أن React لا تتبع حالة الإدخال. تقوم عناصر إدخال HTML بشكل طبيعي بتتبع حالتها كجزء من DOM ، وبالتالي عند إرسال النموذج ، يتعين علينا قراءة القيم من عناصر DOM نفسها.

للقيام بذلك ، تسمح لنا React بإنشاء “مرجع” (مرجع) لربطه بعنصر ، مما يتيح الوصول إلى عقدة DOM الأساسية. دعنا نرى كيفية القيام بذلك:

class SimpleForm extends React.Component {
  constructor(props) {
    super(props);
    
    this.nameEl = React.createRef();
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    alert(this.nameEl.current.value);
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>Name:
          <input type="text" ref={this.nameEl} />
        </label>
        <input type="submit" name="Submit" />
      </form>
    )
  }
}

كما ترون أعلاه ، بالنسبة للمكوِّن القائم على الفئة ، يمكنك تهيئة مرجع جديد في المُنشئ عن طريق الاتصال React.createRef، وتعيينه إلى خاصية مثيل بحيث يكون متاحًا طوال عمر المكون.

من أجل ربط المرجع بإدخال ، يتم تمريره إلى العنصر باعتباره الخاص ref ينسب. بمجرد الانتهاء من ذلك ، يمكن الوصول إلى عقدة DOM الأساسية للمدخلات عبر this.nameEl.current.

دعونا نرى كيف يبدو هذا في مكون وظيفي:

function SimpleForm(props) {
  const nameEl = React.useRef(null);

  const handleSubmit = e => {
    e.preventDefault();
    alert(nameEl.current.value);
  };

  return (
     <form onSubmit={handleSubmit}>
       <label>Name:
         <input type="text" ref={nameEl} />
       </label>
       <input type="submit" name="Submit" />
     </form>
   );
}

لا يوجد فرق كبير هنا ، بخلاف المبادلة createRef ل useRef صنارة صيد.

مثال: نموذج تسجيل الدخول

function LoginForm(props) {
  const nameEl = React.useRef(null);
  const passwordEl = React.useRef(null);
  const rememberMeEl = React.useRef(null);

  const handleSubmit = e => {
    e.preventDefault();

    const data = {
      username: nameEl.current.value,
      password: passwordEl.current.value,
      rememberMe: rememberMeEl.current.checked,
    }

    
    
  };

  return (
     <form onSubmit={handleSubmit}>
       <input type="text" placeholder="username" ref={nameEl} />
       <input type="password" placeholder="password" ref={passwordEl} />
       <label>
         <input type="checkbox" ref={rememberMeEl} />
         Remember me
       </label>
       <button type="submit" className="myButton">Login</button>
     </form>
   );
}

عرض على CodePen

بينما تعمل المدخلات غير المنضبطة بشكل جيد للنماذج السريعة والبسيطة ، إلا أن لها بعض العيوب. كما لاحظت من الكود أعلاه ، يتعين علينا قراءة القيمة من عنصر الإدخال متى أردنا ذلك. هذا يعني أنه لا يمكننا توفير التحقق الفوري من صحة الحقل مثل أنواع المستخدمين ، ولا يمكننا القيام بأشياء مثل فرض تنسيق إدخال مخصص أو إظهار عناصر النموذج أو إخفائها بشكل مشروط أو تعطيل / تمكين زر الإرسال

لحسن الحظ ، هناك طريقة أكثر تعقيدًا للتعامل مع المدخلات في React.

المدخلات الخاضعة للرقابة

يُقال أن المُدخل “مُسيطر عليه” عندما تكون React مسؤولة عن الحفاظ على حالتها وتعيينها. يتم الاحتفاظ بالحالة متزامنة مع قيمة الإدخال ، مما يعني أن تغيير الإدخال سيؤدي إلى تحديث الحالة ، وسيؤدي تحديث الحالة إلى تغيير الإدخال.

لنرى كيف يبدو ذلك بمثال:

class ControlledInput extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: '' };
    this.handleInput = this.handleInput.bind(this);
  }

  handleInput(event) {
    this.setState({
      name: event.target.value
    });
  }

  render() {
    return (
      <input type="text" value={this.state.name} onChange={this.handleInput} />
    );
  }
}

كما ترى ، قمنا بإعداد نوع من تدفق البيانات الدائري: الحالة إلى قيمة الإدخال ، وفي حدث التغيير إلى الحالة ، والعودة مرة أخرى. تتيح لنا هذه الحلقة قدرًا كبيرًا من التحكم في الإدخال ، حيث يمكننا التفاعل مع التغييرات التي تطرأ على القيمة أثناء التنقل. لهذا السبب ، لا تعاني المدخلات الخاضعة للرقابة من قيود المدخلات غير المنضبط ، مما يفتح إمكانيات المتابعة:

  • التحقق الفوري من صحة الإدخال: يمكننا إعطاء المستخدم ملاحظات فورية دون الحاجة إلى انتظاره لإرسال النموذج (على سبيل المثال ، إذا كانت كلمة المرور الخاصة به ليست معقدة بدرجة كافية)
  • تنسيق الإدخال الفوري: يمكننا إضافة فواصل مناسبة لمدخلات العملة ، أو التجميع إلى أرقام الهواتف على الفور
  • تعطيل إرسال النموذج بشكل مشروط: يمكننا تمكين زر الإرسال بعد استيفاء معايير معينة (مثل موافقة المستخدم على الشروط والأحكام)
  • توليد مدخلات جديدة ديناميكيًا: يمكننا إضافة مدخلات إضافية إلى نموذج بناءً على المدخلات السابقة للمستخدم (على سبيل المثال ، إضافة تفاصيل عن أشخاص إضافيين في حجز فندق)

التحقق من الصحة

كما ذكرت أعلاه ، تتيح حلقة التحديث المستمر للمكونات الخاضعة للرقابة إمكانية إجراء تحقق مستمر على المدخلات مثل أنواع المستخدمين. معالج متصل بمدخلات onChange سيتم تشغيل الحدث عند كل ضغطة مفتاح ، مما يتيح لك التحقق من صحة القيمة أو تنسيقها على الفور.

مثال: التحقق من صحة بطاقة الائتمان

دعنا نلقي نظرة على مثال حقيقي للتحقق من رقم بطاقة الائتمان حيث يكتبه المستخدم في نموذج دفع.

يستخدم المثال مكتبة تسمى نوع بطاقة الائتمان لتحديد جهة إصدار البطاقة (مثل Amex أو Visa أو Mastercard) كأنواع المستخدمين. يستخدم المكون بعد ذلك هذه المعلومات لعرض صورة شعار المُصدر بجوار الإدخال:

import  creditCardType  from  "credit-card-type";

function CreditCardForm(props) {
  const [cardNumber, setCardNumber] = React.useState("");
  const [cardTypeImage, setCardTypeImage] = React.useState(
    "card-logo-unknown.svg"
  );

  const handleCardNumber = (e) => {
    e.preventDefault();

    const value = e.target.value;
    setCardNumber(value);

    let suggestion;

    if (value.length > 0) {
      suggestion = creditCardType(e.target.value)[0];
    }

    const cardType = suggestion ? suggestion.type : "unknown";

    let imageUrl;

    switch (cardType) {
      case "visa":
        imageUrl = "card-logo-visa.svg";
        break;
      case "mastercard":
        imageUrl = "card-logo-mastercard.svg";
        break;
      case "american-express":
        imageUrl = "card-logo-amex.svg";
        break;
      default:
        imageUrl = "card-logo-unknown.svg";
    }

    setCardTypeImage(imageUrl);
  };

  return (
    <form>
      <div className="card-number">
        <input
          type="text"
          placeholder="card number"
          value={cardNumber}
          onChange={handleCardNumber}
        />
        <img src={cardTypeImage} alt="card logo" />
      </div>
      <button type="submit" className="myButton">
        Login
      </button>
    </form>
  );
}

المدخلات onChange المعالج يستدعي creditCardType() تعمل بالقيمة الحالية. يؤدي ذلك إلى إرجاع مصفوفة من التطابقات (أو مصفوفة فارغة) يمكن استخدامها لتحديد الصورة المراد عرضها. ثم يتم تعيين عنوان URL للصورة على متغير حالة ليتم عرضه في النموذج.

يمكنك استخدام بعض الأرقام من هنا لاختبار الإدخال.

مكتبات النماذج

كما لاحظت ، هناك قدر معين من كود لوحة الغلاية عند العمل مع النماذج ، خاصة الاضطرار إلى توصيل المدخلات بقيم الحالة والمعالجات. كما قد تتوقع ، هناك مجموعة متنوعة من مكتبات الجهات الخارجية للمساعدة في التخلص من ألم التعامل مع النماذج الأكبر والأكثر تعقيدًا.

لإعطائك فكرة عما يشبه استخدام مكتبة النماذج ، دعنا نلقي نظرة سريعة على واحدة تسمى طازج. الهدف من هذه المكتبة هو تغطية 90٪ من حالات الاستخدام الشائعة بواجهة برمجة تطبيقات بسيطة وسهلة الاستخدام. فيما يلي مثال على نموذج تحرير ملف تعريف قد تجده في تطبيق ويب:

import { Form, Field } from "@leveluptuts/fresh";

const securityQuestions = [
  "What is your mother's maiden name?",
  "What was the name of your first pet?",
  "What was the name of your first school?"
];

const handleSubmit = (data) => console.log(data);

function UserProfileForm() {
  return (
    <Form formId="user-profile" onSubmit={handleSubmit}>
      <Field required>First Name</Field>
      <Field required>Last Name</Field>
      <Field required type="email">
        Email
      </Field>

      <Field required type="select" options={securityQuestions}>
        Security Question
      </Field>
      <Field required>Security Answer</Field>

      <Field type="textarea">Bio</Field>
    </Form>
  );
}

يوفر Fresh بعض المكونات المخصصة لجعل إنشاء النماذج أمرًا سهلاً للغاية. ال Field المكوِّن يعتني بتوصيل ربط البيانات على مدخلات النموذج ، وتحويل الملصق الذي تقدمه إلى اسم خاصية حالة الجمل لقيمة الإدخال. (على سبيل المثال ، يصبح “اسم العائلة” lastName في حالة النموذج.)

ال Form المكون يلتف جميع الحقول ، ويأخذ onSubmit رد الاتصال الذي يتلقى بيانات النموذج ككائن. فيما يلي مثال على الإخراج من النموذج:

{
  firstName: "Bill",
  lastName: "Gates",
  email: "[email protected]",
  securityQuestion: "What was the name of your first pet?",
  securityAnswer: "Fluffy",
  bio: "Bill Gates is a technologist, business leader, and philanthropist. He grew up in Seattle, Washington, with an amazing and supportive family who encouraged his interest in computers at an early age."
}

كما ترى ، يمكن للمكتبات مثل هذه تسريع العمل مع النماذج وجعل مكوناتك أقل إسهابًا. لأي شيء أكثر من نموذج أساسي صغير ، أوصي باختيار نموذج يناسب احتياجاتك ، حيث سيوفر لك الوقت على المدى الطويل.

خاتمة

يجب أن يكون لديك الآن فهم قوي لكيفية استخدام النماذج والمدخلات في React. يجب أن تعرف الفرق بين المدخلات الخاضعة للرقابة وغير المتحكم فيها ، وإيجابيات وسلبيات كل منها ، مع العلم أن حلقة التحديث الضيقة للإدخال المتحكم فيه تتيح لك الكثير من الخيارات لتنسيق القيم والتحقق منها أثناء التنقل. أخيرًا ، يجب أن تدرك أن هناك مكتبات نماذج متاحة تمنعك من الاضطرار إلى إضافة الكثير من التعليمات البرمجية المتكررة والمطولة إلى نماذج React ، مما سيساعدك على أن تكون مطورًا أكثر إنتاجية.

المصدر

أضف تعليق