مقدمة
أثناء العمل على React.js ، غالبًا ما كان علي التعامل مع معالجة النماذج. مرت Redux-Form ، React-Redux-Form من خلال يدي ، ولكن لم تستجب لي إحدى المكتبات تمامًا. لم يعجبني أن حالة النموذج مخزنة في المخفض ، وكل حدث يمر من خلال منشئ الإجراء . أيضًا ، وفقًا لـ Dan Abramov ، "حالة النموذج بطبيعتها سريعة الزوال ومحلية ، لذلك لا تحتاج إلى تتبعها في Redux (أو أي مكتبة Flux)."
ألاحظ أنه في React-Redux-Form هناك مكون LocalForm يسمح لك بالعمل بدون إعادة ، ولكن في رأيي ، لا معنى لتثبيت مكتبة 21.9 كيلوبايت واستخدامها أقل من النصف.
أنا لست ضد المكتبات المسماة ، في حالات معينة لا يمكن الاستغناء عنها. على سبيل المثال ، عندما يعتمد مكون طرف ثالث غير مرتبط بالنموذج على البيانات المدخلة. ولكن في مقالتي ، أريد النظر في النماذج التي لا تحتاج إلى إعادة الاختزال.
لقد بدأت في استخدام الحالة المحلية للمكون ، وظهرت صعوبات جديدة: زادت كمية التعليمات البرمجية ، وفقدت المكونات سهولة القراءة ، وظهر الكثير من الازدواجية.
كان الحل هو مفهوم المكون عالي الترتيب. باختصار ، HOC هي وظيفة تتلقى مدخلات مكون وترجعها محدثة بتكامل الدعائم الإضافية أو المعدلة. اقرأ المزيد عن HOC على موقع React.js الرسمي. كان الغرض من استخدام مفهوم HOC هو تقسيم المكون إلى جزأين ، أحدهما مسؤول عن المنطق ، والثاني - للعرض.
إنشاء النموذج
على سبيل المثال ، سننشئ نموذج ملاحظات بسيطًا يكون فيه 3 حقول: الاسم ، البريد الإلكتروني ، الهاتف.
من أجل البساطة نستخدم Create-React-App . تثبيته عالميًا:
npm i -g create-react-app
ثم قم بإنشاء التطبيق الخاص بك في مجلد الشكل الخالص
create-react-app pure-form
بالإضافة إلى ذلك ، قم بتثبيت أنواع الدعامة وأسماء الفئات ، وسوف تكون مفيدة لنا في المستقبل:
npm i prop-types classnames -S
أنشئ مجلدين / مكونات و / حاويات . سيحتوي المجلد / components على جميع المكونات المسؤولة عن العرض. في المجلد / الحاويات ، المكونات المسؤولة عن المنطق.
في المجلد / components ، قم بإنشاء ملف Input.jsx نعلن فيه عن مكون مشترك لجميع المدخلات. من المهم في هذه المرحلة عدم نسيان وصف ProptTypes و defaultProps بطريقة جيدة ، وتوفير إمكانية إضافة فئات مخصصة ، وكذلك ترثها من PureComponent للتحسين.
والنتيجة هي:
import React, { PureComponent } from 'react'; import cx from 'classnames'; import PropTypes from 'prop-types'; class Input extends PureComponent { render() { const { name, error, labelClass, inputClass, placeholder, ...props } = this.props; return ( <label className={cx('label', !!labelClass && labelClass)} htmlFor={`id-${name}`} > <span className="span">{placeholder}</span> <input className={cx( 'input', !!inputClass && inputClass, !!error && 'error' )} name={name} id={`id-${name}`} onFocus={this.handleFocus} onBlur={this.handleBlur} {...props} /> {!!error && <span className="errorText">{error}</span>} </label> ); } } Input.defaultProps = { type: 'text', error: '', required: false, autoComplete: 'off', labelClass: '', inputClass: '', }; Input.propTypes = { value: PropTypes.string.isRequired, name: PropTypes.string.isRequired, onChange: PropTypes.func.isRequired, placeholder: PropTypes.string.isRequired, error: PropTypes.string, type: PropTypes.string, required: PropTypes.bool, autoComplete: PropTypes.string, labelClass: PropTypes.string, inputClass: PropTypes.string, }; export default Input;
بعد ذلك ، في المجلد / components ، قم بإنشاء الملف Form.jsx ، حيث سيتم الإعلان عن المكون الذي يحتوي على النموذج. سوف نتلقى جميع الطرق للعمل معها من خلال الدعائم ، وكذلك قيمة المدخلات ، لذلك لا حاجة للحالة هنا. نحصل على:
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Input from './Input'; import FormWrapper from '../containers/FormWrapper'; class Form extends Component { render() { const { data: { username, email, phone }, errors, handleInput, handleSubmit, } = this.props; return ( <div className="openBill"> <form className="openBillForm" onSubmit={handleSubmit}> <Input key="username" value={username} name="username" onChange={handleInput} placeholder="" error={errors.username} required /> <Input key="phone" value={phone} name="phone" onChange={handleInput} placeholder="" error={errors.phone} required /> <Input key="email" value={email} type="email" name="email" onChange={handleInput} placeholder=" " error={errors.email} required /> <button type="submit" className="submitBtn"> </button> </form> </div> ); } } Form.propTypes = { data: PropTypes.shape({ username: PropTypes.string.isRequired, phone: PropTypes.string.isRequired, email: PropTypes.string.isRequired, }).isRequired, errors: PropTypes.shape({ username: PropTypes.string.isRequired, phone: PropTypes.string.isRequired, email: PropTypes.string.isRequired, }).isRequired, handleInput: PropTypes.func.isRequired, handleSubmit: PropTypes.func.isRequired, }; export default FormWrapper(Form);
إنشاء HOC
في المجلد / containers ، قم بإنشاء ملف FormWrapper.jsx . نعلن الدالة بالداخل ، والتي تأخذ مكون WrappedComponent كوسيطة وتُرجع فئة WrappedForm . طريقة التجسيد لهذه الفئة تقوم بإرجاع WrappedComponent مع الدعائم المدمجة فيه. حاول استخدام إعلان الوظيفة الكلاسيكية ، سيؤدي ذلك إلى تبسيط عملية التصحيح.
في فئة WrappedForm ، قم بإنشاء الحالة : isFetching - علامة للتحكم في الطلبات غير المتزامنة والبيانات - كائن بقيم الإدخال والأخطاء - كائن لتخزين الأخطاء. يتم تمرير الدولة المعلنة إلى WrappedComponent . وبالتالي ، يتم تنفيذ تنفيذ تخزين حالات النموذج إلى المستوى العلوي ، مما يجعل الشفرة أكثر قابلية للقراءة وشفافية.
export default function Wrapper(WrappedComponent) { return class FormWrapper extends Component { state = { isFetching: false, data: { username: '', phone: '', email: '', }, errors: { username: '', phone: '', email: '', }, }; render() { return <WrappedComponent {...this.state} />; } }; }
لكن هذا التنفيذ ليس عالميًا ، لأنه لكل شكل عليك إنشاء غلافك الخاص. يمكنك تحسين هذا النظام وتضمين HOC داخل دالة أخرى ستشكل قيم الحالة الأولية.
import React, { Component } from 'react'; export default function getDefaultValues(initialState, requiredFields) { return function Wrapper(WrappedComponent) { return class WrappedForm extends Component { state = { isFetching: false, data: initialState, errors: requiredFields, }; render() { return <WrappedComponent {...this.state} {...this.props} />; } }; }; }
في هذه الوظيفة ، لا يمكنك فقط تمرير القيم الأولية للحالة ، ولكن بشكل عام أي معلمات. على سبيل المثال ، السمات والأساليب التي يمكن على أساسها إنشاء نموذج في Form.jsx . مثال على هذا التطبيق سيكون موضوع المقال التالي.
في ملف Form.jsx ، نعلن قيم الحالة الأولية ونمررها إلى HOC :
const initialState = { username: '', phone: '', email: '', }; export default FormWrapper(initialState, initialState)(Form);
لنقم بإنشاء طريقة handleInput لمعالجة القيم التي تم إدخالها في الإدخال. يتلقى حدثًا نأخذ منه القيمة والاسم وننقلهم إلى setState . نظرًا لأنه يتم تخزين قيم الإدخال في كائن البيانات ، فإننا نسمي الوظيفة في setState . بالتزامن مع حفظ القيمة التي تم الحصول عليها ، نقوم بتخزين صفر خطأ تخزين الحقل المتغير. نحصل على:
handleInput = event => { const { value, name } = event.currentTarget; this.setState(({ data, errors }) => ({ data: { ...data, [name]: value, }, errors: { ...errors, [name]: '', }, })); };
الآن سنقوم بإنشاء طريقة handeSubmit لمعالجة النموذج وعرض البيانات في وحدة التحكم ، ولكن قبل ذلك نحتاج إلى اجتياز التحقق من الصحة. سنقوم فقط بالتحقق من صحة الحقول المطلوبة ، أي جميع مفاتيح كائن this.state.errors. نحصل على:
handleSubmit = e => { e.preventDefault(); const { data } = this.state; const isValid = Object.keys(data).reduce( (sum, item) => sum && this.validate(item, data[item]), true ); if (isValid) { console.log(data); } };
باستخدام طريقة التصغير ، نقوم بالفرز عبر جميع الحقول المطلوبة. في كل تكرار ، تسمى طريقة التحقق ، التي نمرر بها اسمًا ، قيمة . داخل الطريقة ، يتم التحقق من صحة البيانات المدخلة ، والتي ترجع نتائجها نوع منطقي. إذا لم ينجح زوج واحد على الأقل من القيم في التحقق من الصحة ، فإن المتغير isValid سيصبح خطأ ولن يتم إخراج البيانات إلى وحدة التحكم ، أي لن تتم معالجة النموذج. يتم النظر في حالة بسيطة هنا - التحقق من وجود نموذج غير فارغ. طريقة التحقق :
validate = (name, value) => { if (!value.trim()) { this.setState( ({ errors }) => ({ errors: { ...errors, [name]: ' ', }, }), () => false ); } else { return true; } };
يجب تمرير طريقتين handleSubmit و handleInput إلى WrappedComponent :
render() { return ( <WrappedComponent {...this.state} {...this.props} handleInput={this.handleInput} handleSubmit={this.handleSubmit} /> ); }
ونتيجة لذلك ، نحصل على نموذج تعليقات جاهز ، مع التحقق البسيط وإخراج الخطأ. في الوقت نفسه ، أزلنا الجزء المنطقي من المكون المسؤول عن العرض.
الخلاصة
لذا ، نظرنا إلى مثال أساسي لإنشاء HOC لمعالجة النماذج. عند إنشاء النموذج ، تم استخدام المدخلات البسيطة فقط ، بدون عناصر معقدة ، مثل القوائم المنسدلة وخانات الاختيار وأزرار الاختيار وغيرها. إذا كان ذلك متاحًا ، فقد يتعين عليك إنشاء طرق معالجة حدث إضافية.
اكتب الأسئلة والتعليقات في التعليقات على المقال أو على بريدي.
يمكن العثور على مثال كامل هنا: شكل رد فعل نقي .
شكرا لكم على اهتمامكم!