Einführung
Während der Arbeit an React.js musste ich mich oft mit der Formularverarbeitung befassen. Redux-Form , React-Redux-Form ging mir durch die Hände, aber keine der Bibliotheken hat mich völlig zufrieden gestellt. Es hat mir nicht gefallen, dass der Status des Formulars im Reduzierer gespeichert ist und jedes Ereignis den Aktionsersteller durchläuft. Dan Abramov zufolge ist "der Status des Formulars von Natur aus kurzlebig und lokal, sodass Sie es nicht in Redux (oder einer anderen Flux-Bibliothek) nachverfolgen müssen."
Ich stelle fest, dass es in React-Redux-Form eine LocalForm- Komponente gibt, mit der Sie ohne Redux arbeiten können. Meiner Meinung nach ist es jedoch nicht sinnvoll, eine 21,9-KB-Bibliothek zu installieren und weniger als die Hälfte zu verwenden.
Ich bin nicht gegen die genannten Bibliotheken, in bestimmten Fällen sind sie unersetzlich. Zum Beispiel, wenn eine Komponente eines Drittanbieters, die nicht mit dem Formular zusammenhängt, von den eingegebenen Daten abhängt. Aber in meinem Artikel möchte ich Formulare betrachten, die keinen Redux benötigen.
Ich fing an, den lokalen Status der Komponente zu verwenden, und es traten neue Schwierigkeiten auf: Die Menge des Codes nahm zu, die Komponenten verloren die Lesbarkeit, es traten viele Duplikate auf.
Die Lösung war das Konzept der Komponente hoher Ordnung. Kurz gesagt, HOC ist eine Funktion, die eine Komponenteneingabe empfängt und diese mit der Integration zusätzlicher oder geänderter Requisiten aktualisiert zurückgibt. Weitere Informationen zu HOC finden Sie auf der offiziellen Website von React.js . Der Zweck der Verwendung des HOC-Konzepts bestand darin, die Komponente in zwei Teile aufzuteilen, von denen einer für die Logik und der zweite für die Anzeige verantwortlich ist.
Formularerstellung
Als Beispiel erstellen wir ein einfaches Feedback-Formular, in dem drei Felder angezeigt werden: Name, E-Mail, Telefon.
Der Einfachheit halber verwenden wir die Create-React-App . Installieren Sie es global:
npm i -g create-react-app
Erstellen Sie dann Ihre Anwendung im reinen Ordner
create-react-app pure-form
Installieren Sie außerdem Requisitentypen und Klassennamen , die uns in Zukunft nützlich sein werden:
npm i prop-types classnames -S
Erstellen Sie zwei Ordner / Komponenten und / Container . Der Ordner / components enthält alle Komponenten, die für die Anzeige verantwortlich sind. Im Ordner / container die für die Logik verantwortlichen Komponenten.
Erstellen Sie im Ordner / components eine Input.jsx- Datei, in der wir eine gemeinsame Komponente für alle Eingaben deklarieren. In dieser Phase ist es wichtig, nicht zu vergessen, ProptTypes und defaultProps qualitativ vorzuschreiben , benutzerdefinierte Klassen hinzuzufügen und diese zur Optimierung von PureComponent zu erben.
Das Ergebnis ist:
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;
Erstellen Sie als Nächstes im Ordner / components die Datei Form.jsx , in der die Komponente, die das Formular enthält, deklariert wird. Wir erhalten alle Methoden für die Arbeit damit durch Requisiten sowie den Wert für Eingaben, sodass hier kein Status benötigt wird. Wir bekommen:
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-Erstellung
Erstellen Sie im Ordner / container die Datei FormWrapper.jsx . Wir deklarieren die darin enthaltene Funktion, die die WrappedComponent- Komponente als Argument verwendet und die WrappedForm- Klasse zurückgibt . Die Rendermethode dieser Klasse gibt eine WrappedComponent mit darin integrierten Requisiten zurück . Versuchen Sie, die klassische Funktionsdeklaration zu verwenden. Dies vereinfacht den Debugging-Prozess.
Erstellen Sie in der WrappedForm- Klasse den Status : isFetching - ein Flag zum Steuern asynchroner Anforderungen, Daten - ein Objekt mit Eingabewerten, Fehler - ein Objekt zum Speichern von Fehlern. Der deklarierte Status wird an die WrappedComponent übergeben . Somit wird die Implementierung der Speicherung von Formularzuständen auf der oberen Ebene implementiert, wodurch der Code lesbarer und transparenter wird.
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} />; } }; }
Eine solche Implementierung ist jedoch nicht universell, da Sie für jedes Formular einen eigenen Wrapper erstellen müssen. Sie können dieses System verbessern und das HOC in eine andere Funktion einbetten, die die Anfangszustandswerte bildet.
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} />; } }; }; }
In dieser Funktion können Sie nicht nur die Anfangswerte des Zustands übergeben , sondern im Allgemeinen alle Parameter. Zum Beispiel Attribute und Methoden, auf deren Grundlage in Form.jsx ein Formular erstellt werden kann . Ein Beispiel für eine solche Implementierung ist das Thema des nächsten Artikels.
In der Datei Form.jsx deklarieren wir die Anfangszustandswerte und übergeben sie an das HOC:
const initialState = { username: '', phone: '', email: '', }; export default FormWrapper(initialState, initialState)(Form);
Erstellen wir die handleInput- Methode zum Verarbeiten der in die Eingabe eingegebenen Werte. Er erhält ein Ereignis, von dem wir Wert und Namen nehmen und das wir an setState übertragen . Da die Eingabewerte im Datenobjekt gespeichert sind, rufen wir die Funktion in setState auf . Gleichzeitig mit dem Speichern des erhaltenen Werts setzen wir die Fehlerspeicherung des Variablenfeldes auf Null. Wir bekommen:
handleInput = event => { const { value, name } = event.currentTarget; this.setState(({ data, errors }) => ({ data: { ...data, [name]: value, }, errors: { ...errors, [name]: '', }, })); };
Jetzt erstellen wir die handeSubmit- Methode zum Verarbeiten des Formulars und zeigen die Daten in der Konsole an. Zuvor müssen wir jedoch die Validierung bestehen. Wir werden nur die erforderlichen Felder überprüfen, dh alle Schlüssel des this.state.errors-Objekts. Wir bekommen:
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); } };
Mit der Reduktionsmethode sortieren wir alle erforderlichen Felder. Bei jeder Iteration wird die Validierungsmethode aufgerufen, an die wir ein Paar von Namen und Werten übergeben . Innerhalb der Methode wird die Richtigkeit der eingegebenen Daten überprüft, deren Ergebnisse den Booleschen Typ zurückgeben. Wenn mindestens ein Wertepaar die Validierung nicht besteht, wird die Variable isValid falsch und die Daten werden nicht an die Konsole ausgegeben, d. H. Das Formular wird nicht verarbeitet. Hier wird ein einfacher Fall betrachtet - eine Prüfung auf ein nicht leeres Formular. Validierungsmethode :
validate = (name, value) => { if (!value.trim()) { this.setState( ({ errors }) => ({ errors: { ...errors, [name]: ' ', }, }), () => false ); } else { return true; } };
Sowohl die handleSubmit- als auch die handleInput-Methode müssen an die WrappedComponent übergeben werden :
render() { return ( <WrappedComponent {...this.state} {...this.props} handleInput={this.handleInput} handleSubmit={this.handleSubmit} /> ); }
Als Ergebnis erhalten wir ein vorgefertigtes Feedback-Formular mit einfacher Validierung und Fehlerausgabe. Gleichzeitig haben wir den logischen Teil aus der für die Anzeige verantwortlichen Komponente entfernt.
Fazit
Daher haben wir uns ein grundlegendes Beispiel für die Erstellung eines HOC für die Verarbeitung von Formularen angesehen. Beim Erstellen des Formulars wurden nur einfache Eingaben ohne komplexe Elemente wie Dropdown-Listen, Kontrollkästchen, Optionsfelder und andere verwendet. Falls verfügbar, müssen Sie möglicherweise zusätzliche Ereignisverarbeitungsmethoden erstellen.
Schreiben Sie Fragen und Kommentare in die Kommentare zum Artikel oder zu meiner Mail.
Ein vollständiges Beispiel finden Sie hier: reine Reaktionsform .
Vielen Dank für Ihre Aufmerksamkeit!