[解決済み] Facebook Reactでコードを再利用するためのミキシンとコンポーネントの使い分け
質問
BackboneプロジェクトでFacebook Reactを使い始めていますが、今のところとてもうまくいっています。
しかし、私はReactのコードに忍び寄るいくつかの重複に気づきました。
例えば
フォームのようなウィジェットがいくつかあるのですが
のような状態を持つ
INITIAL
,
SENDING
と
SENT
. ボタンが押されると、フォームの検証、リクエスト、そしてステートの更新が必要です。ステートはReactの内部で保持されます
this.state
の内部に保持されます。
もしこれがBackboneのビューであれば、私はこのクラスから
FormView
を抽出したはずですが
私の印象では、Reactはビューロジックを共有するためのサブクラス化を推奨もサポートもしていません。
(ということです(間違っていたら訂正してください)。
私はReactでコードの再利用に2つのアプローチを見てきました。
- ミックスイン(例えば LinkedStateMixin のように、Reactに同梱されているもの)。
- コンテナコンポーネント(例えば react-infinite-scroll ).
Reactでは継承よりもミキシンとコンテナが優先されるということでよろしいでしょうか? これは意図的な設計上の決定なのでしょうか? 2段落目の「フォームウィジェット」の例では、ミキシンとコンテナコンポーネントのどちらを使用するのがより理にかなっているのでしょうか?
これは gist で
FeedbackWidget
と
JoinWidget
を現在の状態で
. これらは同じような構造を持ち、同じような
beginSend
メソッドがあり、両方とも何らかの検証サポートが必要です (まだありません)。
どのように解決するのですか?
更新:この回答は古くなっています。できればミキシンは使わないでください。 私はあなたに警告しました!
Mixins Are Dead. コンポジション万歳
最初は、これをサブコンポーネントを使って、抽出しようとしたのですが
FormWidget
と
InputWidget
. しかし、私はこの方法を途中で断念しました。なぜなら、生成された
input
とその状態をよりよく制御したいと思ったからです。
一番役に立った記事2つ
- Reactで考える で考えてみると、実はこのためにネストされたコンポーネントは必要ないことに気づかされました。
- 再利用可能なコンポーネント には、きちんとした mixin の例があります。
結局、2つの(異なる)mixinを書けばいいことがわかりました。
ValidationMixin
と
FormMixin
.
こんな感じで分けてみました。
バリデーションミキシン
バリデーションミキシンは、バリデータ関数をステートのプロパティに対して実行するための便利なメソッドを追加し、「エラーが発生した」プロパティを
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
には3つのメソッドがあります。
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)} />
);
}
フォームミキサー
フォームミキシンはフォームの状態(編集可能、送信、投稿)を処理します。リクエスト送信中は入力やボタンを無効にし、送信されたらそれに応じてビューを更新するために使用することができます。
ソース ( 要点 )
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;
});
使用方法
コンポーネントが1つのメソッドを提供することを想定しています。
sendRequest
このメソッドはブルーバードのプロミスを返す必要があります。(Qや他のプロミスライブラリで動作するように修正するのは些細なことです。)
これは以下のような便利なメソッドを提供します。
isFormEditable
,
isFormSubmitting
と
isFormSubmitted
. また、リクエストをキックオフするためのメソッドも用意されています。
submitForm
. このメソッドは、フォームボタンの
onClick
ハンドラです。
関連
-
[解決済み] 配列の反復処理に "for...in "を使用するのは、なぜ良くないのでしょうか?
-
[解決済み] Reactルータを使ったプログラムによるナビゲーション
-
[解決済み] jQueryを使用してハイパーリンクのhref属性を変更する方法
-
[解決済み] javascriptでの配列の交差のための最も簡単なコード
-
[解決済み] Reactコンポーネントに条件付きで属性を追加するにはどうすればよいですか?
-
[解決済み] React / React Nativeでコンストラクタを使用する場合とgetInitialStateを使用する場合の違いとは何ですか?
-
[解決済み] Reactの "after render "コード?
-
[解決済み】React Formでpropsが変更されたときに状態を更新する
-
[解決済み] Bootstrap モーダル:トグル時に背景がトップにジャンプする
-
[解決済み] jqueryで部分文字列を作成する方法
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] <noscript> の反対語のHTMLはありますか?
-
[解決済み] 文字列の最後の文字を取得するにはどうすればよいですか?重複
-
[解決済み] DataURLからBlob?
-
[解決済み] マングース ユーザーの全リストを取得する
-
[解決済み] SVG のテキスト要素の幅を取得する
-
[解決済み] node.js シェルコマンドの実行
-
[解決済み] async-await from functionを使用して非同期関数から値を返すには?重複
-
[解決済み] Angularで、配列内のオブジェクトを検索したい。
-
[解決済み] 特定のクラスを持たない要素を選択する方法
-
[解決済み] AngularJS - $http.postがJSONの代わりにリクエストパラメータを送信する方法はありますか?