Pembaruan: jawaban ini sudah ketinggalan zaman. Jauhi mixin jika Anda bisa. Saya memperingatkan Anda!
Mixins Are Dead. Komposisi Umur Panjang
Pada awalnya, saya mencoba menggunakan subkomponen untuk ini dan mengekstrak FormWidgetdan InputWidget. Namun, saya mengabaikan pendekatan ini setengah jalan karena saya ingin kontrol yang lebih baik atas yang dihasilkan inputdan statusnya.
Dua artikel yang paling membantu saya:
Ternyata saya hanya perlu menulis dua mixin (berbeda): ValidationMixindan FormMixin.
Begini cara saya memisahkan mereka.
ValidationMixin
Mixin validasi menambahkan metode praktis untuk menjalankan fungsi validator Anda pada beberapa properti negara bagian Anda dan menyimpan properti "error'd" dalam state.errorslarik sehingga Anda dapat menyorot kolom terkait.
Sumber ( inti )
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;
});
Pemakaian
ValidationMixinmemiliki tiga metode: validate, hasErrordan resetError.
Ini mengharapkan kelas untuk mendefinisikan validatorsobjek, mirip dengan propTypes:
var JoinWidget = React.createClass({
mixins: [React.addons.LinkedStateMixin, ValidationMixin, FormMixin],
validators: {
email: Misc.isValidEmail,
name: function (name) {
return name.length > 0;
}
},
// ...
});
Ketika pengguna menekan tombol pengiriman, saya menelepon validate. Panggilan ke validateakan menjalankan setiap validator dan terisi this.state.errorsdengan array yang berisi kunci properti yang gagal validasi.
Dalam rendermetode saya , saya gunakan hasErroruntuk menghasilkan kelas CSS yang benar untuk bidang. Ketika pengguna menempatkan fokus di dalam lapangan, saya memanggil resetErroruntuk menghapus sorotan kesalahan hingga validatepanggilan berikutnya .
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
Form mixin menangani status formulir (dapat diedit, dikirim, dikirim). Anda dapat menggunakannya untuk menonaktifkan input dan tombol saat permintaan dikirim, dan untuk memperbarui tampilan Anda secara bersamaan saat permintaan dikirim.
Sumber ( inti )
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;
});
Pemakaian
Ia mengharapkan komponen untuk menyediakan satu metode:, sendRequestyang harus mengembalikan janji Bluebird. (Sangat mudah untuk memodifikasinya agar berfungsi dengan Q atau library promise lainnya.)
Ini memberikan metode kenyamanan seperti isFormEditable, isFormSubmittingdan isFormSubmitted. Hal ini juga menyediakan metode untuk memulai permintaan: submitForm. Anda dapat menyebutnya dari onClickpenangan tombol formulir .