再利用可能なコンポーネント
インターフェースを設計する際に、共通の設計の要素(ボタン、フォーム・フィールド、レイアウト・コンポーネント等)を、 明確に定義された(well-defined)再利用可能なコンポーネントに分解して下さい。 こうしておくことで次にUIを構築する際にコード量を減らすことが可能になり、 これは開発時間の短縮、バグの減少に繋がります。
propの検証
アプリケーションが肥大化していくと、これはコンポーネントが正しく使用されていることを保証するための手助けになります。
propTypes
の指定を可能にすることで、これを行います。
React.PropTypes
は、受け取ったデータが正しいのかを検証するためのバリデータの範囲を出力します。
prop
に対して不正な値が提供された場合、JavaScriptのコンソール内に警告が表示されます。
パーフォマンス上の理由から、propTypes
は開発モード時にしかチェックされないことに注意して下さい。
下記は、異なるバリデータが提供されている例になります。
React.createClass({
propTypes: {
// propは特定のJSプリミティブ(な型)を宣言することが出来ます。
// デフォルトでは、これらは全て任意になります。
optionalArray: React.PropTypes.array,
optionalBool: React.PropTypes.bool,
optionalFunc: React.PropTypes.func,
optionalNumber: React.PropTypes.number,
optionalObject: React.PropTypes.object,
optionalString: React.PropTypes.string,
// number、string、element、arrayの型を含む、描画可能なもの
optionalNode: React.PropTypes.node,
// React要素
optionalElement: React.PropTypes.element,
// propがクラスのインスタンスであることを宣言することも可能です。
// これには、JavaScriptのinstanceof演算子が使用されます。
optionalMessage: React.PropTypes.instanceOf(Message),
// enumのように扱うことで、propが特定のいずれかの値であることを
// 確かめることが出来ます。
optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
// オブジェクトが複数の型のいずれかであることを確かめることが出来ます。
optionalUnion: React.PropTypes.oneOfType([
React.PropTypes.string,
React.PropTypes.number,
React.PropTypes.instanceOf(Message)
]),
// 特定の型の配列
optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
// 特定の型のプロパティを持つオブジェクト
optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
// 独自のスタイルを取得するオブジェクト
optionalObjectWithShape: React.PropTypes.shape({
color: React.PropTypes.string,
fontSize: React.PropTypes.number
}),
// 上述したものにisRequiredを繋げることで
// そのpropが提供されなかった場合に警告を表示します。
requiredFunc: React.PropTypes.func.isRequired,
// 任意の型
requiredAny: React.PropTypes.any.isRequired,
// カスタム・バリデータも指定可能です。
// 検証が失敗した際には、Errorオブジェクトを返す必要があります。
// oneOfType内部では動作しないため、console.warnまたはthrowは使用しないで下さい。
customProp: function(props, propName, componentName) {
if (!/matchme/.test(props[propName])) {
return new Error('Validation failed!');
}
}
},
/* ... */
});
デフォルトのprop値
Reactは、とても宣言的な方法で、props
にデフォルトの値を定義させます。
var ComponentWithDefaultProps = React.createClass({
getDefaultProps: function() {
return {
value: 'default value'
};
}
/* ... */
});
getDefaultProps()
の結果はキャッシュされ、
もし、親コンポーネントから値の指定が無かったとしても、this.props.value
が値を持つことを保証してくれます。
これは、あなた自身が繰り返しまた脆弱なコードを書くこと無く、安全にpropsを使用出来るようにしてくれます。
propsの受け渡し: ショートカット
Reactコンポーネントの一般的な型は、シンプルな方法で基本的なHTML要素を拡張したものの1つです。 あなたは、コンポーネントに渡される属性を、 タイピングを減らすために基盤となるHTML要素へコピーしたくなるでしょう。 JSXのスプレッド文法を使用すれば、これを行うことが可能です。
var CheckLink = React.createClass({
render: function() {
// CheckLinkに渡されたpropsを取得し、<a>にコピーします。
return <a {...this.props}>{'√ '}{this.props.children}</a>;
}
});
React.render(
<CheckLink href="/checked.html">
Click here!
</CheckLink>,
document.getElementById('example')
);
単一の子
React.PropTypes.element
を使用して、子としてコンポーネントへ渡される単一の子であることを指定する事が出来ます。
var MyComponent = React.createClass({
propTypes: {
children: React.PropTypes.element.isRequired
},
render: function() {
return (
<div>
{this.props.children} // 厳密に1つの要素でなければいけません。
// そうでなければスロー(throw)されます。
</div>
);
}
});
ミックスイン
コンポーネントはReactでコードを再利用するための最善の方法ですが、
稀に全く異なるコンポーネントが幾つかの共通の機能を共有する事があるかもしれません。
これらは、クロスカット関係(?)(cross-cutting concerns)と呼ばれることがあります。
Reactはこの問題を解決するために、mixins
を提供します。
一般的なユースケースの1つとして、インターバル毎に自身の更新を行いたいコンポーネントが挙げられます。
setInterval()
を使用することは簡単ですが、メモリを節約するために必要が無くなった際に、
そのインターバルをキャンセルするという事が重要になります。
Reactは、コンポーネントが作成される際に、または削除される際に、
その事を知らせてくれるライフサイクル・メソッドを提供します。
これらのメソッドを使用して簡単なsetInterval()
関数を提供して、
コンポーネントが削除された際に自動的にクリーンアップされるシンプルなミックスインを作成してみましょう。
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.map(clearInterval);
}
};
var TickTock = React.createClass({
mixins: [SetIntervalMixin], // ミックスインを使用
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // ミックスイン上でメソッドを呼び出し
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
React.render(
<TickTock />,
document.getElementById('example')
);
ミックスインの素晴らしい機能の1つに、もしコンポーネントが複数のミックスインを使用し、 幾つかのミックスインが同じライフサイクル・メソッドを定義する場合(つまり、幾つかのミックスインは、そのコンポーネントが削除された際に何らかのクリーンアップを行いたい)、 ライフサイクル・メソッド全ての呼び出しが保証されます。 ミックスイン上で定義されたメソッドは、コンポーネント上のメソッド呼び出しに続いて、 ミックスインでリスト化された順に実行されます。
ES6 クラス
素のJavaScriptのクラスとして、Reactのクラスを定義しても構いません。 例えば、下記はES6のクラス文法を使用した例になります。
class HelloMessage extends React.Component {
render() {
return <div>Hello {this.props.name}</div>;
}
}
React.render(<HelloMessage name="Sebastian" />, mountNode);
このAPIは、getInitialState
を除き、React.createClass
に似ています。
getInitialState
メソッドを提供する代わりに、
コンストラクタ内で自身のstate
プロパティを設定します。
もう1つの違いに、propTypes
とdefaultProps
がクラス本文では無く、
コンストラクタにプロパティとして定義する事が挙げられます。
export class Counter extends React.Component {
constructor(props) {
super(props);
this.state = {count: props.initialCount};
}
tick() {
this.setState({count: this.state.count + 1});
}
render() {
return (
<div onClick={this.tick.bind(this)}>
Clicks: {this.state.count}
</div>
);
}
}
Counter.propTypes = { initialCount: React.PropTypes.number };
Counter.defaultProps = { initialCount: 0 };
オート・バインディング無し
メソッドは通常のES6クラスとして同じセマンティクスをフォローし、
これは自動的にインスタンスへthis
をバインドしないことを意味します。
使用するには、明示的に.bind(this)
、またはアロー関数(arrow function)を使用する必要があります。
ミックイン無し
残念ながら、ES6にはミックスインのサポートはありません。 そのため、ES6クラスをReactで使用する際にもミックスインはサポートされません。 その代わりに、このようなユースケースをサポートするために、 ミックスインに頼らなくてもそれが簡単に行えるための取り組みが行われています。