الفرق بين المراجعتين لصفحة: «React/forms»

من موسوعة حسوب
لا ملخص تعديل
تحديث
 
(17 مراجعة متوسطة بواسطة 3 مستخدمين غير معروضة)
سطر 1: سطر 1:
<noinclude>{{DISPLAYTITLE:الحقول}}</noinclude>
<noinclude>{{DISPLAYTITLE:الحقول في React}}</noinclude>
تعمل عناصر الحقول (forms) بشكلٍ مختلفٍ قليلًا عن بقيّة عناصر DOM الأخرى في React بسبب احتفاظ عناصر الحقول بشكل طبيعي بحالة داخلية خاصّة بها. فمثلًا يقبل هذا الحقل في HTML اسمًا واحدًا:<syntaxhighlight lang="javascript">
تعمل عناصر [[HTML/form|الحقول]] (forms) بشكلٍ مختلفٍ قليلًا عن بقيّة عناصر DOM الأخرى في React بسبب احتفاظ عناصر [[HTML/form|الحقول]] بشكل طبيعي بحالة داخلية خاصّة بها. فمثلًا يقبل هذا الحقل في [[HTML]] اسمًا واحدًا:<syntaxhighlight lang="javascript">
<form>
<form>
   <label>
   <label>
سطر 6: سطر 6:
     <input type="text" name="name" />
     <input type="text" name="name" />
   </label>
   </label>
   <input type="submit" value="تقديم البيانات" />
   <input type="submit" value="إرسال" />
</form>
</form>
</syntaxhighlight>يمتلك هذا الحقل نفس السّلوك الافتراضي لحقول HTML من حيث الانتقال إلى صفحة جديدة عندما يضغط المستخدم على زر تقديم البيانات (Submit)، وإن أردت فقط هذا السّلوك في React فسيعمل بشكل جيّد معك، ولكن في معظم الأحيان من الملائم أكثر أن نمتلك دالة في JavaScript تتعامل مع تقديم البيانات ولديها الوصول إلى البيانات التي أدخلها المستخدم في الحقل. الطريقة القياسيّة لتحقيق هذا الأمر هي باستخدام تقنيّة تدعى المُكوِّنات المضبوطة (controlled components).
</syntaxhighlight>يمتلك هذا الحقل نفس السّلوك الافتراضي [[HTML/form|لحقول HTML]] من حيث الانتقال إلى صفحة جديدة عندما يضغط المستخدم على زر إرسال البيانات (Submit)، وإن أردت فقط هذا السّلوك في React فسيعمل بشكل جيّد معك، ولكن في معظم الأحيان من الملائم أكثر أن نمتلك دالة في JavaScript تتعامل مع تقديم البيانات ولديها الوصول إلى البيانات التي أدخلها المستخدم في الحقل. الطريقة القياسيّة لتحقيق هذا الأمر هي باستخدام تقنيّة تدعى المُكوِّنات المضبوطة (controlled components).


== المكونات المضبوطة ==
== المكونات المضبوطة ==
تُحافِظ عناصر الحقول في HTML مثل <code>[[HTML/input|<input>]]</code>، و <code>[[HTML/textarea|<textarea>]]</code>، و <code>[[HTML/select|<select>]]</code> على حالتها الخاصّة وتُحدِثها وفقًا لمُدخلات المستخدم. أمّا في React فيُحتفَظ بحالة قابلة للتعديل ضمن خاصيّة الحالة للمُكوِّنات وتُحدَّث فقط عن طريق التابع [[React/react component|setState()‎]].
تُحافِظ عناصر الحقول في [[HTML]] مثل <code>[[HTML/input|<input>]]</code>، و <code>[[HTML/textarea|<textarea>]]</code>، و <code>[[HTML/select|<select>]]</code> على حالتها الخاصّة وتُحدِثها وفقًا لمُدخلات المستخدم. أمّا في React فيُحتفَظ بحالة قابلة للتعديل ضمن خاصيّة الحالة للمُكوِّنات وتُحدَّث فقط عن طريق التابع <code>[[React/react component|setState()‎]]</code>.


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


على سبيل المثال إن أردنا في المثال السّابق أن نعرض الاسم بعد تقديمه فبإمكاننا كتابة الحقل كمُكوِّن مضبوط:
على سبيل المثال إن أردنا في المثال السّابق أن نعرض الاسم بعد تقديمه فبإمكاننا كتابة الحقل كمُكوِّن مضبوط:<syntaxhighlight lang="javascript">
class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};
 
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
 
  handleSubmit(event) {
    alert('قُدِّم الاسم: ' + this.state.value);
    event.preventDefault();
  }
 
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          الاسم:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="إرسال" />
      </form>
    );
  }
}
</syntaxhighlight>[https://codepen.io/gaearon/pen/VmmPgp?editors=0010 جرب المثال على موقع CodePen].
 
لمّا كانت خاصيّة القيمة <code>value</code> مُعيَّنة عن طريق عنصر الحقل فستكون قيمتها المعروضة دومًا هي <code>this.state.value</code>، وبذلك نجعل حالة React المصدر الوحيد للحقيقة. وبما أنّ التابع <code>handleChange</code> يُنفَّذ عند كل ضغطة زر من المستخدم ليُحدِّث حالة React، فستتحدّث القيمة المعروضة بينما يكتب المستخدم.
 
في المكونات المضبوطة يكون لكل تغيير للحالة دالة للتعامل مع ذلك مُوافِقة له. صحيح أنّ هذا يتطلب كتابة شيفرات أكثر، إلا أنه سيكون بمقدورك تمرير القيم إلى العناصر الرسومية الأخرى، أو إعادة تعيينها من معالجات أحداث أخرى. يجعل ذلك من تعديل دخل المستخدم والتحقّق منه أمرًا سهلًا، فمثلًا لو أردنا إجبار المستخدم على كتابة الأحرف بحالة كبيرة سنكتب الدالة <code>handleChange</code> كما يلي:<syntaxhighlight lang="javascript">
handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});
}
</syntaxhighlight>
 
== العنصر <code>textarea</code> ==
في HTML يُعرَّف نص العنصر <code>[[HTML/textarea|<textarea>]]</code> بشكلٍ مباشر كما يلي:<syntaxhighlight lang="html">
<textarea>
  مرحبًا، هذا نص ما موجود ضمن العنصر textarea
</textarea>
</syntaxhighlight>أمّا في React يستخدم العنصر <code>[[HTML/textarea|<textarea>]]</code> الخاصيّة <code>value</code> بدلًا من ذلك، وبهذه الطريقة يُمكِن كتابة الحقل الذي يستخدم <code>[[HTML/textarea|<textarea>]]</code> بشكلٍ مُشابِه للحقل الذي يستخدم عنصر الإدخال <code>[[HTML/input|<input>]]</code>:<syntaxhighlight lang="javascript">
class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'اكتب من فضلك مقالًا حول العنصر المفضّل لديك في DOM'
    };
 
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
 
  handleSubmit(event) {
    alert('قُدِّم المقال: ' + this.state.value);
    event.preventDefault();
  }
 
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          المقال:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="إرسال" />
      </form>
    );
  }
}
</syntaxhighlight>لاحظ أنّنا هيَّأنا <code>this.state.value</code> بقيمة مبدئيّة في الدالة البانية (constructor)، وبذلك نضمن وجود نص ضمن العنصر  <code>[[HTML/textarea|<textarea>]]</code> منذ البداية.
 
== العنصر <code>select</code> ==
في HTML يُنشِئ العنصر <code>[[HTML/select|<select>]]</code> قائمة مُنسدِلة، فمثلًا تُنشِئ هذه الشيفرة قائمة مُنسدِلة ببعض أسماء الفاكهة:<syntaxhighlight lang="html">
<select>
  <option value="Banana">موز</option>
  <option value="apple">تفّاح</option>
  <option selected value="Orange">برتقال</option>
  <option value="mango">مانجو</option>
</select>
</syntaxhighlight>لاحظ أنّ الخيار المبدئي هنا هو البرتقال بسبب وجود الخاصيّة <code>selected</code> بجانبه، ولكن في React بدلًا من استخدام الخاصيّة <code>selected</code> نستخدم الخاصيّة <code>value</code> ضمن العنصر <code>[[HTML/select|<select>]]</code>  وهذا أسهل في المُكوِّنات المضبوطة لأنّك ستحتاج لتعديلها فقط في مكانٍ واحد. على سبيل المثال:<syntaxhighlight lang="javascript">
class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'orange'};
 
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
 
  handleChange(event) {
    this.setState({value: event.target.value});
  }
 
  handleSubmit(event) {
    alert('فاكهتك المفضّلة هي: ' + this.state.value);
    event.preventDefault();
  }
 
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          اختر فاكهتك المفضّلة
          <select value={this.state.value} onChange={this.handleChange}>
              <option value="banana">موز</option>
  <option value="apple">تفّاح</option>
  <option value="orange">برتقال</option>
  <option value="mango">مانجو</option>
          </select>
        </label>
        <input type="submit" value="إرسال" />
      </form>
    );
  }
}
</syntaxhighlight>[https://codepen.io/gaearon/pen/JbbEzX?editors=0010 جرِّب المثال على موقع CodePen].
 
وبهذا نجد أنّ العناصر ‎<code>[[HTML/input|<input type="text"‎>]]</code>‎ و  <code>[[HTML/textarea|<textarea>]]</code> و  <code>[[HTML/select|<select>]]</code> تعمل بشكلٍ مماثل، فجميعها تقبل الخاصيّة <code>value</code> والتي نستخدمها لتنفيذ المُكوِّن المضبوط.
 
'''ملاحظة:''' بإمكانك تمرير مصفوفة إلى الخاصيّة <code>value</code> حيث يُتيح لك ذلك انتقاء عدّة خيارات في العنصر <code>[[HTML/select|<select>]]</code> :<syntaxhighlight lang="html">
<select multiple={true} value={['B', 'C']}>
</syntaxhighlight>
 
== عنصر إدخال الملفات ==
في HTML يُتيح العنصر ‎<code><input type="file"></code>‎ للمستخدم أن يختار ملفًّا واحدًا أو أكثر من جهازه لتحميلها إلى الخادم أو التعامل معها عن طريق JavaScript وذلك عبر [https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications واجهة برمجة التطبيق الخاصّة بالملفّات]:<syntaxhighlight lang="html">
<input type="file" />
</syntaxhighlight>وبما أنّ قيمته هي قابلة للقراءة فقط فهو مُكوِّن غير مضبوط (uncontrolled component) في React، سنناقش هذا المُكوِّن مع المُكوِّنات غير المضبوطة الأخرى في [[React/uncontrolled components|قسمها الخاص]].
 
== التعامل مع إدخالات متعددة ==
عندما تحتاج إلى التعامل مع عناصر <code>input</code> مُتعدِّدة مضبوطة فبإمكانك إضافة الخاصيّة <code>name</code> إلى كل عنصر وتترك لدالة معالجة الأحداث أن تختار ما ستفعله بناءً على قيمة <code>event.target.name</code>، فلنأخذ هذا المثال:<syntaxhighlight lang="javascript">
class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };
 
    this.handleInputChange = this.handleInputChange.bind(this);
  }
 
  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;
 
    this.setState({
      [name]: value
    });
  }
 
  render() {
    return (
      <form>
        <label>
          الذاهبون:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          عدد الضيوف:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}
</syntaxhighlight>[https://codepen.io/gaearon/pen/wgedvV?editors=0010 جرّب المثال على موقع CodePen].
 
لاحظ كيف استخدمنا صياغة اسم الخاصيّة المحسوب في ES6 لتحديث مفتاح الحالة بما يُوافِق الاسم المُدخَل:<syntaxhighlight lang="javascript">
this.setState({
  [name]: value
});
</syntaxhighlight>يُكافِئ الشيفرة السّابقة في ES5 ما يلي:<syntaxhighlight lang="javascript">
var partialState = {};
partialState[name] = value;
this.setState(partialState);
</syntaxhighlight>بما أنّ الدالة <code>setState()</code>‎ [[React/state and lifecycle#.D8.AA.D8.AF.D9.85.D8.AC React .D8.AA.D8.AD.D8.AF.D9.8A.D8.AB.D8.A7.D8.AA .D8.A7.D9.84.D8.AD.D8.A7.D9.84.D8.A9|تدمج تلقائيًّا حالة جزئيّة مع الحالة الحاليّة]] سنحتاج فقط إلى استدعائها مع الأجزاء المتغيّرة.
 
== الإدخالات المضبوطة ذات القيمة <code>Null</code> ==
يمنع تحديد الخاصيّة <code>value</code> في [[React/forms#.D8.A7.D9.84.D9.85.D9.83.D9.88.D9.86.D8.A7.D8.AA .D8.A7.D9.84.D9.85.D8.B6.D8.A8.D9.88.D8.B7.D8.A9|المُكوِّنات المضبوطة]] المستخدم من تغيير المُدخلات ما لم ترغب بذلك. إن حدَّدت القيمة <code>value</code> وبقي العنصر <code>input</code> قابلًا للتعديل فربّما قد عيّنت <code>value</code> إلى القيمة <code>undefined</code> أو <code>null</code> من غير قصد.
 
تُوضِّح الشيفرة التالية هذا (يكون العنصر <code>input</code> مقفولًا في البداية ثم يُصبِح قابلًا للتعديل بعد فترة زمنيّة قصيرة):<syntaxhighlight lang="javascript">
ReactDOM.render(<input value="hi" />, mountNode);
 
setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);
</syntaxhighlight>
 
== بدائل المكونات المضبوطة ==
قد يُصبِح استخدام المكونات المضبوطة مُمِلًّا أحيانًا لأنّك تحتاج إلى كتابة مُعالِج أحداث لكل طريقة قد تتغيّر بها بياناتك وإلى توجيه جميع حالات الإدخال عبر مُكوِّن React. يُصبِح هذا مُزعجًا بشكلٍ خاص عند تحويل الشيفرة الموجودة سابقًا إلى React أو عند دمج تطبيق React مع مكتبة أخرى. في هذه الحالات قد ترغب باستخدام [[React/uncontrolled components|المُكوِّنات غير المضبوطة (uncontrolled components)]]، وهي تقنيّة بديلة للتعامل مع حقول الإدخال.
 
== حلول متكامل ==
إن كنت تبحث عن حل متكامل يتضمن التحقق من الصحة، تتبع الحقول المزارة، ومعالجة إرسال الحقل، يعدُّ [https://jaredpalmer.com/formik Formik] أحد الخيارات المشهورة. على أي حال، إنََّه مبني على نفس مبادئ مكونات التحكم وإدارة الحالة، لذا لا تهمل هذا الأمر وتفوت فرصة تعلمها.
 
== انظر أيضًا ==
* [[React/hello world|مثال أهلًا بالعالم في React]]
* [[React/introducing jsx|مقدمة إلى JSX]]
* [[React/rendering elements|تصيير العناصر]]
* [[React/components and props|المكونات والخاصيات]]
* [[React/state and lifecycle|حالة ودورة حياة المكونات]]
* [[React/handling events|معالجة الأحداث في React]]
* [[React/conditional rendering|التصيير الشرطي]]
* [[React/lists and keys|القوائم والمفاتيح]]
* [[React/lifting state up|رفع الحالات المشتركة للمستوى الأعلى]]
* [[React/composition vs inheritance|الفرق بين التركيب والوراثة في React]]
* [[React/thinking in react|أسلوب التفكير في React]]
 
== مصادر==
*[https://reactjs.org/docs/forms.html صفحة الحقول في توثيق React الرسمي].
[[تصنيف:React]]
[[تصنيف:React Main Concepts]]

المراجعة الحالية بتاريخ 16:29، 2 نوفمبر 2020

تعمل عناصر الحقول (forms) بشكلٍ مختلفٍ قليلًا عن بقيّة عناصر DOM الأخرى في React بسبب احتفاظ عناصر الحقول بشكل طبيعي بحالة داخلية خاصّة بها. فمثلًا يقبل هذا الحقل في HTML اسمًا واحدًا:

<form>
  <label>
    الاسم:
    <input type="text" name="name" />
  </label>
  <input type="submit" value="إرسال" />
</form>

يمتلك هذا الحقل نفس السّلوك الافتراضي لحقول HTML من حيث الانتقال إلى صفحة جديدة عندما يضغط المستخدم على زر إرسال البيانات (Submit)، وإن أردت فقط هذا السّلوك في React فسيعمل بشكل جيّد معك، ولكن في معظم الأحيان من الملائم أكثر أن نمتلك دالة في JavaScript تتعامل مع تقديم البيانات ولديها الوصول إلى البيانات التي أدخلها المستخدم في الحقل. الطريقة القياسيّة لتحقيق هذا الأمر هي باستخدام تقنيّة تدعى المُكوِّنات المضبوطة (controlled components).

المكونات المضبوطة

تُحافِظ عناصر الحقول في HTML مثل <input>، و <textarea>، و <select> على حالتها الخاصّة وتُحدِثها وفقًا لمُدخلات المستخدم. أمّا في React فيُحتفَظ بحالة قابلة للتعديل ضمن خاصيّة الحالة للمُكوِّنات وتُحدَّث فقط عن طريق التابع setState()‎.

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

على سبيل المثال إن أردنا في المثال السّابق أن نعرض الاسم بعد تقديمه فبإمكاننا كتابة الحقل كمُكوِّن مضبوط:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('قُدِّم الاسم: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          الاسم:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="إرسال" />
      </form>
    );
  }
}

جرب المثال على موقع CodePen.

لمّا كانت خاصيّة القيمة value مُعيَّنة عن طريق عنصر الحقل فستكون قيمتها المعروضة دومًا هي this.state.value، وبذلك نجعل حالة React المصدر الوحيد للحقيقة. وبما أنّ التابع handleChange يُنفَّذ عند كل ضغطة زر من المستخدم ليُحدِّث حالة React، فستتحدّث القيمة المعروضة بينما يكتب المستخدم.

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

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()});
}

العنصر textarea

في HTML يُعرَّف نص العنصر <textarea> بشكلٍ مباشر كما يلي:

<textarea>
  مرحبًا، هذا نص ما موجود ضمن العنصر textarea
</textarea>

أمّا في React يستخدم العنصر <textarea> الخاصيّة value بدلًا من ذلك، وبهذه الطريقة يُمكِن كتابة الحقل الذي يستخدم <textarea> بشكلٍ مُشابِه للحقل الذي يستخدم عنصر الإدخال <input>:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'اكتب من فضلك مقالًا حول العنصر المفضّل لديك في DOM'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('قُدِّم المقال: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          المقال:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="إرسال" />
      </form>
    );
  }
}

لاحظ أنّنا هيَّأنا this.state.value بقيمة مبدئيّة في الدالة البانية (constructor)، وبذلك نضمن وجود نص ضمن العنصر <textarea> منذ البداية.

العنصر select

في HTML يُنشِئ العنصر <select> قائمة مُنسدِلة، فمثلًا تُنشِئ هذه الشيفرة قائمة مُنسدِلة ببعض أسماء الفاكهة:

<select>
  <option value="Banana">موز</option>
  <option value="apple">تفّاح</option>
  <option selected value="Orange">برتقال</option>
  <option value="mango">مانجو</option>
</select>

لاحظ أنّ الخيار المبدئي هنا هو البرتقال بسبب وجود الخاصيّة selected بجانبه، ولكن في React بدلًا من استخدام الخاصيّة selected نستخدم الخاصيّة value ضمن العنصر <select> وهذا أسهل في المُكوِّنات المضبوطة لأنّك ستحتاج لتعديلها فقط في مكانٍ واحد. على سبيل المثال:

class FlavorForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: 'orange'};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

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

  handleSubmit(event) {
    alert('فاكهتك المفضّلة هي: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          اختر فاكهتك المفضّلة
          <select value={this.state.value} onChange={this.handleChange}>
              <option value="banana">موز</option>
			  <option value="apple">تفّاح</option>
			  <option value="orange">برتقال</option>
			  <option value="mango">مانجو</option>
          </select>
        </label>
        <input type="submit" value="إرسال" />
      </form>
    );
  }
}

جرِّب المثال على موقع CodePen.

وبهذا نجد أنّ العناصر ‎<input type="text"‎>‎ و <textarea> و <select> تعمل بشكلٍ مماثل، فجميعها تقبل الخاصيّة value والتي نستخدمها لتنفيذ المُكوِّن المضبوط.

ملاحظة: بإمكانك تمرير مصفوفة إلى الخاصيّة value حيث يُتيح لك ذلك انتقاء عدّة خيارات في العنصر <select> :

<select multiple={true} value={['B', 'C']}>

عنصر إدخال الملفات

في HTML يُتيح العنصر ‎<input type="file">‎ للمستخدم أن يختار ملفًّا واحدًا أو أكثر من جهازه لتحميلها إلى الخادم أو التعامل معها عن طريق JavaScript وذلك عبر واجهة برمجة التطبيق الخاصّة بالملفّات:

<input type="file" />

وبما أنّ قيمته هي قابلة للقراءة فقط فهو مُكوِّن غير مضبوط (uncontrolled component) في React، سنناقش هذا المُكوِّن مع المُكوِّنات غير المضبوطة الأخرى في قسمها الخاص.

التعامل مع إدخالات متعددة

عندما تحتاج إلى التعامل مع عناصر input مُتعدِّدة مضبوطة فبإمكانك إضافة الخاصيّة name إلى كل عنصر وتترك لدالة معالجة الأحداث أن تختار ما ستفعله بناءً على قيمة event.target.name، فلنأخذ هذا المثال:

class Reservation extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      isGoing: true,
      numberOfGuests: 2
    };

    this.handleInputChange = this.handleInputChange.bind(this);
  }

  handleInputChange(event) {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.setState({
      [name]: value
    });
  }

  render() {
    return (
      <form>
        <label>
          الذاهبون:
          <input
            name="isGoing"
            type="checkbox"
            checked={this.state.isGoing}
            onChange={this.handleInputChange} />
        </label>
        <br />
        <label>
          عدد الضيوف:
          <input
            name="numberOfGuests"
            type="number"
            value={this.state.numberOfGuests}
            onChange={this.handleInputChange} />
        </label>
      </form>
    );
  }
}

جرّب المثال على موقع CodePen. لاحظ كيف استخدمنا صياغة اسم الخاصيّة المحسوب في ES6 لتحديث مفتاح الحالة بما يُوافِق الاسم المُدخَل:

this.setState({
  [name]: value
});

يُكافِئ الشيفرة السّابقة في ES5 ما يلي:

var partialState = {};
partialState[name] = value;
this.setState(partialState);

بما أنّ الدالة setState()تدمج تلقائيًّا حالة جزئيّة مع الحالة الحاليّة سنحتاج فقط إلى استدعائها مع الأجزاء المتغيّرة.

الإدخالات المضبوطة ذات القيمة Null

يمنع تحديد الخاصيّة value في المُكوِّنات المضبوطة المستخدم من تغيير المُدخلات ما لم ترغب بذلك. إن حدَّدت القيمة value وبقي العنصر input قابلًا للتعديل فربّما قد عيّنت value إلى القيمة undefined أو null من غير قصد.

تُوضِّح الشيفرة التالية هذا (يكون العنصر input مقفولًا في البداية ثم يُصبِح قابلًا للتعديل بعد فترة زمنيّة قصيرة):

ReactDOM.render(<input value="hi" />, mountNode);

setTimeout(function() {
  ReactDOM.render(<input value={null} />, mountNode);
}, 1000);

بدائل المكونات المضبوطة

قد يُصبِح استخدام المكونات المضبوطة مُمِلًّا أحيانًا لأنّك تحتاج إلى كتابة مُعالِج أحداث لكل طريقة قد تتغيّر بها بياناتك وإلى توجيه جميع حالات الإدخال عبر مُكوِّن React. يُصبِح هذا مُزعجًا بشكلٍ خاص عند تحويل الشيفرة الموجودة سابقًا إلى React أو عند دمج تطبيق React مع مكتبة أخرى. في هذه الحالات قد ترغب باستخدام المُكوِّنات غير المضبوطة (uncontrolled components)، وهي تقنيّة بديلة للتعامل مع حقول الإدخال.

حلول متكامل

إن كنت تبحث عن حل متكامل يتضمن التحقق من الصحة، تتبع الحقول المزارة، ومعالجة إرسال الحقل، يعدُّ Formik أحد الخيارات المشهورة. على أي حال، إنََّه مبني على نفس مبادئ مكونات التحكم وإدارة الحالة، لذا لا تهمل هذا الأمر وتفوت فرصة تعلمها.

انظر أيضًا

 مصادر