Оновлення: ця відповідь застаріла. Тримайтеся подалі від міксинів, якщо можете. Я тебе попередив!
Міксини мертві. Склад довгого життя
Спочатку я спробував використовувати для цього підкомпоненти і витягнути FormWidget
і InputWidget
. Однак я відмовився від цього підходу на півдорозі, тому що хотів кращого контролю над генерованими input
та їх державою.
Дві статті, які мені найбільше допомогли:
Виявилося, що мені потрібно було написати лише два (різні) міксини: ValidationMixin
і FormMixin
.
Ось як я їх розділив.
ВалідаціяMixin
Валідація mixin додає методи зручності для запуску функцій валідатора на деяких властивостях вашого штату та зберігання властивостей "error'd" у state.errors
масиві, щоб ви могли виділити відповідні поля.
Джерело ( суть )
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;
});
Використання
ValidationMixin
має три методи: validate
, hasError
і resetError
.
Він очікує, що клас визначить validators
об'єкт, подібний до propTypes
:
var JoinWidget = React.createClass({
mixins: [React.addons.LinkedStateMixin, ValidationMixin, FormMixin],
validators: {
email: Misc.isValidEmail,
name: function (name) {
return name.length > 0;
}
},
// ...
});
Коли користувач натискає кнопку подання, я дзвоню validate
. Виклик до validate
запуску кожного валідатора та заповнення this.state.errors
масивом, який містить ключі властивостей, які не виконали перевірку.
У своєму render
методі я використовую hasError
для створення правильного класу CSS для полів. Коли користувач ставить фокус усередині поля, я закликаю resetError
видалити виділення помилок до наступногоvalidate
дзвінка.
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
Форма mixin обробляє стан форми (можна редагувати, подавати, подавати). Ви можете використовувати його для відключення входів та кнопок під час надсилання запиту та відповідного оновлення перегляду під час надсилання.
Джерело ( суть )
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;
});
Використання
Він очікує, що компонент забезпечить один метод: sendRequest
який повинен повернути обіцянку Bluebird. (Тривіально змінювати його для роботи з Q або іншою бібліотекою обіцянок.)
Він надає зручні методи , такі як isFormEditable
, isFormSubmitting
і isFormSubmitted
. Вона також забезпечує спосіб штовхнути запит: submitForm
. Ви можете зателефонувати йому через onClick
обробник кнопок форми .