在Facebook React中使用mixins与组件重用代码
我开始在一个Backbone项目中使用Facebook React,到目前为止这个过程非常顺利。
但是,我注意到有一些重复进入我的React代码。
例如, 我有几个像INITIAL
, SENDING
和SENT
等状态的窗体小部件 。 当一个button被按下,表单需要被validation,一个请求被做,然后状态被更新。 状态保存在里面反应this.state
,当然还有字段值。
如果这些是骨干视图,我会提取一个名为FormView
的基类,但我的印象是,React既不支持也不支持子类共享视图逻辑 (纠正我,如果我错了)。
我已经看到了两种在React中重用代码的方法:
- Mixins (如React附带的LinkedStateMixin );
- 容器组件(例如react-infinite-scroll )。
我正确的mixin和容器是首选在Reactinheritance? 这是一个故意的devise决定? 使用混合或容器组件为第二段的“表单控件”示例更有意义吗?
这是FeedbackWidget
和JoinWidget
处于当前状态的要点 。 他们有一个类似的结构,类似的beginSend
方法,都需要有一些validation支持(还没有)。
更新:这个答案已经过时了。 如果可以的话,远离混合。 我警告过你!
Mixins已经死了 万岁组成
起初,我试图使用子组件来提取FormWidget
和InputWidget
。 但是,我放弃了这种方法,因为我想更好地控制生成的input
和它们的状态。
两篇文章对我最有帮助:
- React中的思考让我意识到我实际上不需要嵌套的组件。
- 可重用组件有一个纯粹的mixin的例子。
事实certificate,我只需要写两个(不同的)混合: ValidationMixin
和FormMixin
。
以下是我如何分开他们。
ValidationMixin
validationmixin添加了一些便利的方法来在你的某些状态属性上运行你的validation器函数,并在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; } }, // ... });
当用户按下提交button,我打电话validate
。 调用validate
将运行每个validation器,并使用包含未通过validation的属性的键的数组填充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处理表单状态(可编辑,提交,提交)。 您可以在发送请求时使用它来禁用input和button,并在发送时相应地更新您的视图。
来源( 要点 )
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
,它应该返回一个蓝鸟的承诺。 (将其修改为与Q或其他承诺库一起使用是微不足道的)。
它提供了便利的方法,如isFormEditable
, isFormSubmitting
和isFormSubmitted
。 它还提供了一个启动请求的方法: submitForm
。 你可以从窗体button的onClick
处理程序中调用它。
我正在用React构build一个SPA(从1年开始生产),而且我几乎从不使用mixins。
我目前用于mixin的唯一用例是当你想共享使用React的生命周期方法( componentDidMount
等)的行为。 这个问题是由丹·阿布拉莫夫在他的链接 (或通过使用ES6类inheritance)中谈到的高阶组件解决的。
Mixins也经常用在框架中,通过使用React的“hidden” 上下文特性 ,使框架API可用于所有组件。 这与ES6类inheritance不再需要了。
其他大部分时间,mixin被使用,但是并不是真正需要的,可以用简单的助手来代替easiner。
例如:
var WithLink = React.createClass({ mixins: [React.addons.LinkedStateMixin], getInitialState: function() { return {message: 'Hello!'}; }, render: function() { return <input type="text" valueLink={this.linkState('message')} />; } });
你可以很容易地重构LinkedStateMixin
代码,这样的语法是:
var WithLink = React.createClass({ getInitialState: function() { return {message: 'Hello!'}; }, render: function() { return <input type="text" valueLink={LinkState(this,'message')} />; } });
有什么大的区别?