我开始在Backbone项目中使用Facebook React,到目前为止一切进展顺利。 但是,我注意到一些重复出现在我的React代码中。
例如, 我有几个 状态为INITIAL,SENDING和 的类似表单的小部件SENT。当按下按钮时,需要验证表单,提出请求,然后更新状态。this.state当然,状态与字段值一起保存在React 中。
INITIAL
SENDING
SENT
this.state
如果这些是Backbone视图,我会提取一个名为的基类,FormView但 我的印象是React既不认可也不支持子类共享视图逻辑 (如果我错了,请纠正我)。
FormView
我已经看到了两种在React中重用代码的方法:
我是否正确认为Mixins和容器在React中优先于继承?这是故意设计的决定吗? 在第二段的“表单小部件”示例中使用混合或容器组件是否更有意义?
这里有一个要点FeedbackWidget,并JoinWidget在其当前状态。它们具有相似的结构,相似的beginSend方法,并且都需要某种验证支持(尚不存在)。
FeedbackWidget
JoinWidget
beginSend
更新:这个答案已经过时了。如果可以的话,请远离mixin。我警告过你! Mixins已死。 万岁组成
首先,我尝试为此使用子组件并提取FormWidget和InputWidget。但是,我中途放弃了这种方法,因为我想更好地控制生成的inputs及其状态。
FormWidget
InputWidget
input
有两篇对我最有帮助的文章:
事实证明,我只需要编写两个(不同的)mixin:ValidationMixin和FormMixin。 这是我分开它们的方式。
ValidationMixin
FormMixin
验证mixin添加了方便的方法,可以在状态的某些属性上运行验证器函数,并将“错误的”属性存储在state.errors数组中,以便突出显示相应的字段。
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:
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一个数组,该数组包含验证失败的属性的键。
this.state.errors
在我的render方法中,我用来hasError为字段生成正确的CSS类。当用户将焦点放在该字段内时,我会调用resetError以删除错误突出显示,直到下一次validate调用为止。
render
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)} /> ); }
表单混合处理表单状态(可编辑,提交,已提交)。您可以使用它在发送请求时禁用输入和按钮,并在发送请求时相应地更新视图。
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 Promise。(修改它以与Q或其他Promise库一起使用是微不足道的。)
sendRequest
它提供了便利的方法,例如isFormEditable,isFormSubmitting和isFormSubmitted。它还提供了启动请求的方法:submitForm。您可以从表单按钮的onClick处理程序中调用它。
isFormEditable
isFormSubmitting
isFormSubmitted
submitForm
onClick