Update: Diese Antwort ist veraltet. Halte dich von den Mixins fern, wenn du kannst. Ich habe dich gewarnt!
Mixins sind tot. Es lebe die Komposition
Zuerst versuchte ich Subkomponenten für dieses und Extrakt zu verwenden FormWidget
und InputWidget
. Ich habe diesen Ansatz jedoch auf halbem Weg aufgegeben, weil ich eine bessere Kontrolle über die generierten input
s und ihren Zustand haben wollte.
Zwei Artikel, die mir am meisten geholfen haben:
Es stellte sich heraus, dass ich nur zwei (verschiedene) Mixins schreiben musste: ValidationMixin
und FormMixin
.
So habe ich sie getrennt.
ValidationMixin
Das Validierungsmixin fügt praktische Methoden hinzu, mit denen Sie Ihre Validierungsfunktionen für einige Eigenschaften Ihres Staates ausführen und "fehlerhafte" Eigenschaften in einem state.errors
Array speichern können, damit Sie entsprechende Felder hervorheben können.
Quelle ( Kern )
define(function () {
'use strict';
var _ = require('underscore');
var ValidationMixin = {
getInitialState: function () {
return {
errors: []
};
},
componentWillMount: function () {
this.assertValidatorsDefined();
},
assertValidatorsDefined: function () {
if (!this.validators) {
throw new Error('ValidatorMixin requires this.validators to be defined on the component.');
}
_.each(_.keys(this.validators), function (key) {
var validator = this.validators[key];
if (!_.has(this.state, key)) {
throw new Error('Key "' + key + '" is defined in this.validators but not present in initial state.');
}
if (!_.isFunction(validator)) {
throw new Error('Validator for key "' + key + '" is not a function.');
}
}, this);
},
hasError: function (key) {
return _.contains(this.state.errors, key);
},
resetError: function (key) {
this.setState({
'errors': _.without(this.state.errors, key)
});
},
validate: function () {
var errors = _.filter(_.keys(this.validators), function (key) {
var validator = this.validators[key],
value = this.state[key];
return !validator(value);
}, this);
this.setState({
'errors': errors
});
return _.isEmpty(errors);
}
};
return ValidationMixin;
});
Verwendung
ValidationMixin
drei Methoden hat: validate
, hasError
und resetError
.
Es wird erwartet, dass die Klasse ein validators
Objekt definiert , ähnlich wie propTypes
:
var JoinWidget = React.createClass({
mixins: [React.addons.LinkedStateMixin, ValidationMixin, FormMixin],
validators: {
email: Misc.isValidEmail,
name: function (name) {
return name.length > 0;
}
},
// ...
});
Wenn der Benutzer die Übermittlungstaste drückt, rufe ich an validate
. Bei einem Aufruf von validate
wird jeder Validator ausgeführt und this.state.errors
mit einem Array gefüllt, das Schlüssel der Eigenschaften enthält, bei denen die Validierung fehlgeschlagen ist.
In meiner render
Methode hasError
generiere ich die richtige CSS-Klasse für Felder. Wenn der Benutzer den Fokus in das Feld legt, rufe ich resetError
an, um die Fehlerhervorhebung bis zum nächsten validate
Aufruf zu entfernen .
renderInput: function (key, options) {
var classSet = {
'Form-control': true,
'Form-control--error': this.hasError(key)
};
return (
<input key={key}
type={options.type}
placeholder={options.placeholder}
className={React.addons.classSet(classSet)}
valueLink={this.linkState(key)}
onFocus={_.partial(this.resetError, key)} />
);
}
FormMixin
Das Formular-Mixin behandelt den Formularstatus (bearbeitbar, übermittelnd, übermittelt). Sie können es verwenden, um Eingaben und Schaltflächen zu deaktivieren, während eine Anforderung gesendet wird, und um Ihre Ansicht beim Senden entsprechend zu aktualisieren.
Quelle ( Kern )
define(function () {
'use strict';
var _ = require('underscore');
var EDITABLE_STATE = 'editable',
SUBMITTING_STATE = 'submitting',
SUBMITTED_STATE = 'submitted';
var FormMixin = {
getInitialState: function () {
return {
formState: EDITABLE_STATE
};
},
componentDidMount: function () {
if (!_.isFunction(this.sendRequest)) {
throw new Error('To use FormMixin, you must implement sendRequest.');
}
},
getFormState: function () {
return this.state.formState;
},
setFormState: function (formState) {
this.setState({
formState: formState
});
},
getFormError: function () {
return this.state.formError;
},
setFormError: function (formError) {
this.setState({
formError: formError
});
},
isFormEditable: function () {
return this.getFormState() === EDITABLE_STATE;
},
isFormSubmitting: function () {
return this.getFormState() === SUBMITTING_STATE;
},
isFormSubmitted: function () {
return this.getFormState() === SUBMITTED_STATE;
},
submitForm: function () {
if (!this.isFormEditable()) {
throw new Error('Form can only be submitted when in editable state.');
}
this.setFormState(SUBMITTING_STATE);
this.setFormError(undefined);
this.sendRequest()
.bind(this)
.then(function () {
this.setFormState(SUBMITTED_STATE);
})
.catch(function (err) {
this.setFormState(EDITABLE_STATE);
this.setFormError(err);
})
.done();
}
};
return FormMixin;
});
Verwendung
Es wird erwartet, dass die Komponente eine Methode bereitstellt : sendRequest
, die ein Bluebird-Versprechen zurückgeben sollte. (Es ist trivial, es so zu ändern, dass es mit Q oder einer anderen Versprechungsbibliothek funktioniert.)
Es bietet komfortable Methoden wie isFormEditable
, isFormSubmitting
und isFormSubmitted
. Es bietet auch eine Methode zum Starten der Anforderung : submitForm
. Sie können es über den onClick
Handler der Formularschaltflächen aufrufen .