Оновлення: ця відповідь застаріла. Тримайтеся подалі від міксинів, якщо можете. Я тебе попередив!
Міксини мертві. Склад довгого життя
Спочатку я спробував використовувати для цього підкомпоненти і витягнути 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обробник кнопок форми .