React: 保守しやすいハイパフォーマンスの UI コンポーネントを作成する

React JavaScript ライブラリーを使用してブラウザーの DOM を拡張し、保守の容易な Web UI を実現する

UI ビュー・コンポーネントを作成して組み合わせ、今風の Web UI を構成するという作業を容易にするために、オープンソースの JavaScript ライブラリー React を使い始めてください。このチュートリアルの例に従い、保守しやすく再利用可能な React コンポーネントを作成するために定められたベスト・プラクティスについて学んでください。

Sing Li, Consultant, Makawave

Photo of Sing LiSing Li は、developerWorks サイトの開設以来、Web や Java に関するさまざまなトピックの記事とチュートリアルを書いている developerWorks の著者です。組み込みシステムからスケーラブルなエンタープライズ・システムに至るまで、20 年を超えるシステム・エンジニアリングの経験があり、現在は再び Web 規模のモバイル対応サービスと「モノのインターネット」エコシステムに取り組んでいます。



2015年 6月 04日

最近のすべてのブラウザーでサポートされていて、紛れもなく Web の事実上のネイティブ・プログラミング言語となっている JavaScript では、Web-as-a-Platform が提供する豊富な機能のすべてに直接アクセスすることができます。複雑な JavaScript プロジェクトは、コーディングの達人が単独で作業するような規模を超えて、大規模なチームで複数の開発者が協力して取り組むようなものになってきていますが、このようにシフトすることに関連して問題が生じています。具体的には、臨機応変にコードに変更を加えるという形では、他のメンバーがコードを理解して保守するのが不可能になります。新しくチームに加わったメンバーは、複雑なシステムを把握するまでにかなりの時間がかかる可能性もあります。また、拡大し続けるコード・ベースをチーム全体で再利用することができないという問題もあります。ライブ・システムにだけ、安定せず捉えどころのないパフォーマンスの異常が生じることも珍しくありません。

これらの問題を最も熟知しているのは、最先端のソーシャル・ネットワークを提供している企業の他にありません。ソーシャル・ネットワークでは、毎日何百万もの人々が使用する、競争力がある一方で複雑な Web UI を提供しなければなりません。そこで、Facebook/Instagram が 2013年にオープンソースとしてリリースしたのが、UI コンポーネントを作成するためのライブラリーである React プロジェクトです。React からは、プロジェクトが大きくなっていく際の困難に企業が対処する方法を垣間見ることができます。React と、このプロジェクトに関連するベスト・プラクティスは、偶然にも同じ困難に取り組んでいる JavaScript 開発者たちに共感されて熱烈に受け入れられました。

このチュートリアルでは、React について紹介するとともに、React の動作と React に関して定められたベスト・プラクティスについて説明し、サンプル・コードを使って迅速なコーディングができるようにします (サンプル・コードを入手するには、「ダウンロード」セクションを参照してください)。このチュートリアルを終えると、皆さんは独自の再利用可能な React コンポーネントを作成することや、コミュニティーが貢献した数百ものオープンソース・コンポーネントから新しいコンポーネントを組み立てることができるようになります。

関連チュートリアル「React を使用して IBM Watson エクスプローラーを作成する」では、React とオープンソース・コンポーネントを使用して本格的なネットワーク・アプリケーションを作成する方法を紹介しています。

再利用可能な UI コンポーネントを作成するための React

Web UI コンポーネントの階層を宣言によって指定して、その階層を React の仮想 DOM に渡します。その後は、React が適時、UI をブラウザーの実際の DOM と同期させます。

React は、JSX コンパイラーおよび関連する開発者向けツールを備えた JavaScript ライブラリーとして配布されており、再利用可能なハイパフォーマンスの UI ビュー・コンポーネントを容易に作成できるようにします。そうして作成したコンポーネントを組み合わせて、今風の Web UI を構成することも可能です。定められたベスト・プラクティスに従うことで、保守しやすく再利用可能なコンポーネントを作成することができます。

ランタイム・パフォーマンスを最適化するために、React コンポーネントは最初に管理対象の仮想 DOM にレンダリングされます (図 1 を参照)。

図 1. React の動作
React の動作を示す図

保持モードでの動作

React の仮想 DOM は、保持モードで動作します。UI がどのように表示され、どのように動作するかを (レンダリングするコンポーネントの JSX シーン・グラフという形で) 宣言によって記述すると、ランタイム・エンジンがその UI を表示する最適な方法を見つけ出します。保持モードで動作させると、ハイパフォーマンスでのレンダリングの最適化が可能になるため、3D グラフィックス (よく使われているゲーム・エンジンや WebGL フレームワークなど) を扱う場合には保持モードで動作させるのが通常です。保持モードでの動作は、Famo.us などの最近のアプリケーション・フレームワークでも採用されています。

Web UI コンポーネントの階層を宣言によって指定して、その階層を React の仮想 DOM に渡します。その後は、React が適時、UI をブラウザーの実際の DOM と同期させます。React の仮想 DOM 実装は自己完結型であり、ブラウザーに依存しません。この実装は、サーバー・サイドのレンダリングにでさえ使用することができます (「参考文献」を参照)。仮想 DOM は、その内部の状態とブラウザーの DOM との差分を最適化した形で取り、UI の整合性を維持するために最低限必要な更新を行います。ブラウザーの DOM 要素に直接変更を加えて一度に 1 つの視覚的な遷移を行う場合、それに伴うパフォーマンス問題のうち大規模なものは、この保持モードの手法 (囲み記事「保持モードでの動作」を参照) によって回避されます (これは jQuery UI などのよく使用されている UI コンポーネント・ライブラリーの動作と同様です)。

React は、同様のライブラリーとは異なり、従来のモデル・ビュー・コントローラー (MVC) スタイルの UI 構造と状態管理を強制していません。代わりに React が唯一重点を置いているのは、ビューの構造です。このプロジェクトでは、複雑な Web UI の作成には MVC が最適とは限らない理由について、見解を公開しています (囲み記事「複雑な今風の UI に対する MVC の落とし穴」を参照)。ただし、React で MVC 構造の使用を阻むものはありません。多くの開発者は初めて React を使用する際に、既存の大規模な MVC ベースのプロジェクトのビュー・レイヤーに新規コードを組み込んでいます。

複雑な今風の UI に対する MVC の落とし穴

一度にページを表示する従来の Web アプリケーションには、ページごとに必要な MVC インスタンスは 2 つか 3 つで済みますが、高度にインタラクティブな UI コンポーネントを使用する最近のシングル・ページのアプリケーションでは、相互接続された多数の MVC のインスタンスを作成して保守しなければなりません。これらの MVC は、相互に入り組んだ形で接続されます。例えば、複数のコントローラーが共通のモデルを共有していたり、1 つのコントローラーが動作時に別のコントローラーに依存したりするといった形です。

モデル・インスタンスは、複数のコントローラーが直接または間接的に共有する可変のアプリケーション状態を表すインメモリー・データ構造となって現れる場合がよくあります。こうした状況では、保守するのが困難なネズミの巣のようなモデル・データ (そしてモデル・データの計算バージョンと派生バージョン) が作成されて MVC ネットワークで受け渡される可能性があります。そうなると、管理対象データのその時点での唯一の真のソースを判断するのが難しくなります。React チームはアンチパターンのケース・スタディーとして、入り組んだ MVC ネットワークで「連鎖する更新」を追跡する難しさを挙げています。

さらにもう 1 つの JavaScript UI ライブラリーを試そうとする動機

React の魅力的な特徴には以下のものが含まれます。

  • React を習得するのは簡単です。JavaScript に精通しているとしたら、半日もあれば、このライブラリーを扱えるようになります。
  • 実証済みのベスト・プラクティスに記されている、コンポーネントを作成する知恵に従えば、大規模な JavaScript コード・ベースでも保守および管理をすることが可能になります。
  • 最近の JavaScript 開発者のあらゆる思い付きに対処するために、React ではほとんどのローダーとツールベルト (AMD、CommonJS、その他さまざまなツール (gulp、bower、npm、browserify、webpack、grunt) が動作するようになっています。
  • React の採用は、リスクを伴うオール・オア・ナッシングの提案ではありません。このライブラリーは、ほとんどの既存のコード・ベース (これには従来の MVC コード・ベースでさえも含まれます) とともに動作し、既存の UI フレームワークとも適切に統合されます (「参考文献」を参照)。
  • React の設計は、非同期のバックエンド・アーキテクチャーと完璧に結合するため、将来のテクノロジーにも対応することができます。
  • React ランタイムはパフォーマンスに重点を置いていますが、(React Native を使用して) iOS や Android などの他のターゲット・プラットフォームに拡張することも可能です。

JSX による宣言型 UI の記述とロジックの統合

JSX は、拡張 JavaScript 構文を受け入れるトランスフォーマー兼コンパイラーです。例えば、以下のような HTML 風の拡張表記があるとします。

<div>
     <MyLabel  text={TextLabel} />
     <MyTextfield />
     <MyButton textlabel='OK' />
</div>

JSX は上記の表記を以下の JavaScript React API 呼び出しに変換します。

React.createElement("div", null, 
     React.createElement(MyLabel, {text: TextLabel}), 
     React.createElement(MyTextfield, null), 

     React.createElement(MyButton, {textlabel: "OK"}))

オンラインで JSX を試してみることができます

インタラクティブなオンライン JSX コンパイラーをいろいろと試して、JSX の詳細を学ぶことができます。

お馴染みの HTML に似た JSX の表記により、React コンポーネントのコーディングと保守はかなり簡素化されています。この簡素化は、深くネストされたコンポーネントの関係を宣言する際に、特に明らかになります。

JSX により、UI ロジックを関連する構造的な記述と一緒にまとめて、すべて単一のファイルに保管することが可能になります。これは、生産性を向上させるのに役立つと同時に、大規模なプロジェクトのエラーを削減するのにも役立ちます。他のフレームワークを使用する場合、同期させなければならないファイル (テンプレート・ファイル、ハンドラー JavaScript コード・ファイル、HTML 構造記述ファイル) の数は最大で 3 倍になります。

JSX をデプロイする

JSX はブラウザー内で実行することも、単独で実行することもできます。開発の段階では、JSX コードに変更を加えた後、すぐにその結果を確認できるブラウザー内のトランスフォーマーが便利です。本番環境では、最善のパフォーマンスを得るために、スタンドアロンのトランスフォーマーをビルド・ツールチェーンに組み込むのが当然の選択肢となります。

わかりやすくするために、このチュートリアルに記載するすべてのサンプル・コード (「ダウンロード」を参照) では、一貫してブラウザー内のトランスフォーマーを使用します。最初のサンプル・コード (example1) は、コンテンツ・デリバリー・ネットワーク (CDN) から React とトランスフォーマーの両方を取得します。これは、React を稼働中の状態にさせる (Web サーバーの背後にファイルを配置して、それぞれの URL にアクセスできるようにする) 最も簡単な方法です。

<html lang="en">
<head>
<script src="http://fb.me/react-0.12.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
<script type="text/jsx" src="example1.jsx"></script>
</head>
<body>

example2 と example3 は、bower パッケージ・マネージャーを使用して React をインストールします。これらのサンプル・コードは、ローカルで取得したブラウザー内の JSX トランスフォーマーのコピーを参照します。例えば、example2.html では以下のコードを使用して 2 つのファイルを取得します。

<script src="bower_components/react/react-with-addons.js"></script>
<script src="bower_components/react/JSXTransformer.js"></script>

いずれにしても、JSX ファイルに変更を加え、ブラウザーを最新表示に更新して結果を表示することで、React を試すことができます。


カスタム React コンポーネントを作成する

React を使用して短時間でコンポーネントを作成するには、以下の example1.jsx のコードを使用します。

var MyTextfield = React.createClass({
  render: function() {
    return <input type='text' />;
  }
});

たったこれだけのコードでコンポーネントを作成することができます。

この単純なカスタム React コンポーネント (図 2 に、他の React コンポーネントと一緒に示されています) は、使用する HTML の <input> 要素をラップして、「非制御」入力フィールドをレンダリングします。これにより、このコンポーネントを <MyTextfield> として使用できるようになります。ネイティブ HTML 要素の名前は小文字で始まる一方、カスタム React コンポーネント (クラス) の名前は大文字で始まることに注意してください。

図 2. React の <MyTextfield><MyLabel>、および <MyButton> コンポーネント
最初の React コンポーネントのスクリーンショット

React コンポーネントの props と JSX における JavaScript の式

example1 の 2 番目のカスタム・コンポーネントは <MyButton> です。

var MyButton = React.createClass({
  render: function() {
    return <button>{this.props.textlabel}</button>;
  }
});

<MyButton> は、カスタマイズされた HTML <button> 要素をレンダリングするコンポーネントです。このコンポーネントは、受信されるプロパティー (React の用語では、「props」と呼びます) をデモンストレーションしています。props はコンポーネントが受け取る引数であり、HTML タグに設定される属性とまったく同様です。<MyButton> コンポーネントはこれらの属性にアクセスすることで、this.props を使用してレンダリング方法をカスタマイズすることができます。プロパティー値をレンダリングするには、{ <JavaScript の式> } を使ってインラインの JavaScript の式を評価する JSX のサポートを利用することができます。

図 2 の <MyButton> インスタンスは、textlabel プロパティーに設定された OK を使用してパラメーター化されています。具体的には、<MyButton textlabel='OK' /> という形です。

React コンポーネントを組み合わせる

コンポーネントを組み合わせることは、UI コンポーネントを再利用する上での基礎となります。React では、既存の React コンポーネントをネイティブ HTML 要素と併せて組み合わせることで、より複雑なコンポーネントを簡単に作成することができます。

example1 では、カスタム React コンポーネント (<MyTextfield><MyButton>、および <MyLabel>) を UI 内で組み合わせています。JSX ではこれを以下のように簡単に行うことができます。

React.render(
  <div>
    <MyLabel  text={TextLabel} />
     <MyTextfield />
     <MyButton textlabel='OK' />
 </div>,
  document.getElementById('container')
);

組み合わされたコンポーネントは、このコードに先行する API 呼び出しにより、仮想 DOM 内の container という ID の <div> 要素内でレンダリングされます。

props としての関数と React の SyntheticEvent

要素の属性は、単純なデータ型ではなくコールバック関数でパラメーター化されることがよくあります。例として、皆さんは HTML の <button> 要素の onClick 属性を設定した経験があることでしょう。それと同じ手法を、React コンポーネントにも適用することができます。2 番目のサンプル・コード (example2.jsx) では、カスタム <MyButton> React コンポーネントがそのプロパティーの 1 つとして、onClick ハンドラー・コールバックを取ります。

var MyButton = React.createClass({
  _buttonClicked:  function(e) {
    if (this.props.onClick) {
        this.props.onClick(e)
    }
  },
  render: function() {
    return 
     <button onClick={this._buttonClicked}>
             {this.props.textlabel}</button>;
  }
});

React の自動バインド

鋭い読者の皆さんはお気付きだと思いますが、React コンポーネントのコールバック・ハンドラーには bind(this) 呼び出しが使用されていません。それでも、ロジックでこれを参照すると、正しく機能します。その理由は、React はコンポーネントのすべてのメソッドを自動的にインスタンスにバインドするためです。この機能は、自動バインド (autobinding) と呼ばれます。

ボタンをクリックすると、HTML の <button> 要素の onClick イベントが起動されます。すると、React はこのイベントを <MyButton>_buttonClicked() 実装に転送します。

知っておくべき重要な点として、_buttonClicked() は、ネイティブ・ブラウザーの DOM イベントで呼び出されるのではなく、React 固有の SyntheticEvent という特定のブラウザーに依存しない W3C 準拠のオブジェクトによって呼び出されます。SyntheticEvent は、各種のブラウザーで同じように動作し、実際の DOM から生成されたままのイベントをラップします (ラップされた DOM イベントには nativeEvent プロパティーでアクセスすることができます。SyntheticEvent の詳細に関するリンクは、「参考文献」を参照してください)。

_buttonClicked() 内のコードでは、<MyButton> 自身の onClick プロパティーが設定されていることを確認してから、SyntheticEvent をそのイベント・ハンドラーに転送します。

React コンポーネント内で状態を維持する

一部のコンポーネントは、レンダリング中に使用される内部状態を維持する必要があります。例えば、チェック・ボックス・コンポーネントには、自身が選択されたことを覚えておくための状態が必要です。

example2.jsx の <MyTextfield> コンポーネントが維持する内部状態 (data という名前の変数) には、テキスト・フィールドに入力された最新の数値が常に反映されます。状態変数に初期値を提供するには、getInitialState() を実装します。<MyTextfield>data1 に初期化されます。

var MyTextfield = React.createClass({
  getInitialState: function() {
     return {
      data: '1'
     };
  }, 
...

React コンポーネントの状態にアクセスするには、this.state を使用します。<input> 要素の値をレンダリングするには、常に this.state.data を使用します。

render: function() {
  return <input type='text' onChange={this._entryChanged} 
                value={this.state.data} />;
}

この例は、React コンポーネントを作成するための典型的なパターンであり、コンポーネントの状態値と props 値を使用して、コンポーネントをレンダリングまたはカスタマイズします。

この <MyTextfield> の場合、<input> 要素は「制御」されます。それは、(example1 での「非制御」バージョンの <MyTextfield> とは対照的に) この要素の値は常に React コンポーネントによってレンダリングされるからです。

example2 を試してみる: 動的な表ジェネレーター

<MyTextfield> と example2 の仕組みについて理解を深めるために、サンプル・コードを試してみてください。それには、example2 ファイルを Web サーバーの背後に配置して、example2.html にアクセスします。図 3 に、最初に表示される内容を示します。this.state.data の初期値は 1 なので、「Number of rows (行数)」は 1 に設定されます。

図 3. カスタムの動的な表ジェネレーター・コンポーネント
カスタムの動的な表ジェネレーター・コンポーネントのスクリーンショット

テキスト・フィールドに入力できるのは、1 から 9 までの値です。それ以外の値を入力しても、受け付けてくれません。「OK」ボタンをクリックすると、右側に表示された表に、指定した数だけ行が生成されます。図 4 では 9 行生成されています。

図 4. 表ジェネレーターによって表示された 9 行
カスタムの動的な表ジェネレーター・コンポーネントによって生成された 9 行のスクリーンショット

テキスト・フィールドに値を入力すると、<input>onChange イベントが起動され、React がそのイベントを <MyTextfield>_entryChanged() ハンドラーにルーティングします。

_entryChanged: function(e) {
    var val = e.target.value;
    var validated = '1';
    if (!(isNaN(val) || (parseInt(val) > 9 ) || (parseInt(val) < 1)))     {
      validated = val;
    } 
    this.setState({data: validated});
    if (this.props.onChange) {
      this.props.onChange(validated);
    }
  }

入力された値は、_entryChanged() によって必ず 1 から 9 までの値になります。この範囲外の値は 1 に設定されます。_entryChanged() はその後、this.setState() を使用して状態 (data) を更新します。data が新しい値に設定されている場合、次に React によって render() が呼び出されるときに、テキスト・フィールドの内容が更新されます。その結果、<MyTextfield> の内容は常に data 状態変数と同期した内容になります。

この動作も、React コンポーネントでよく見られるパターンの 1 つです。つまり、イベント・ハンドラーが状態変数を変更し、それによって、レンダリングされるコンポーネントの表示を変更するというパターンです。

指定された rows プロパティーに応じて表の行をレンダリングするのは、<DynamicList> React コンポーネントの役目です (詳細については、ソース・コードを参照)。テーブルの各セルは <MyTableCell> インスタンスであり、このインスタンスが、無効にされている <input> 要素をラップします。

他のコンポーネントと同じく重要なコンポーネントである <ListCreator> は、<MyTextfield><MyButton>、および <DynamicList> を組み合わせて最終的な UI を作成します。<MyButton> インスタンスがクリックされると、_okClicked() ハンドラーはその状態値を、(<MyTextfield> の値を追跡する) data と (<DynamicList> 内の行をレンダリングするために使用される) rows の間にコピーします。このようにイベント・ハンドラー内で状態を更新することで、<DynamicList> がその表示行をレンダリングするようになります。

shouldComponentUpdatePureRenderMixin によるレンダリング最適化

仮想 DOM のレンダリングはすでに高速化されているとは言え、レンダリング・パスの間で変更されない UI の部分は、レンダリングされないようにするのが賢明です。

React には、あらゆるコンポーネントが実装できる shouldComponentUpdate というコールバックがあります。前のレンダリング以降に変更が行われていないことが確実であれば、コンポーネントは false を返すことによって、不要なレンダリングを回避することができます。デフォルトでは、コンポーネントは常に true を返します。

example2 の <ListCreator> コンポーネントは、以下のように React の PureRenderMixin アドオンを使用しています。

var ListCreator = React.createClass({
  mixins: [React.addons.PureRenderMixin],
  ...

このミックスインによって追加される shouldComponentUpdate の実装は、すべての props 値および状態値に関して、前の値と現在の値との簡単な比較を行います。変更が見つからなければ、false を返します。これで、不要なレンダリングは排除されます。<ListCreator> のように、コンポーネントの表示が完全に props 値と状態値に依存している場合は、ミックスインがレンダリングのパフォーマンス最適化に役立つはずです。


サード・パーティーの再利用可能な React コンポーネントを使用する

大規模で活発な React コミュニティーの利点の 1 つは、すぐに使用できる状態の React コンポーネントが豊富にあることです。GitHub などのオープンソース・リポジトリーをちょっと検索すれば、調べる価値のあるコンポーネントが何百も見つかります。

example3 では、サード・パーティーの棒グラフ描画用 React コンポーネント (Mateus Zitelli によって作成された react-bar-chart) を使用して UI を作成しています。このサンプル・コードによって表示されるのは、年齢層別に分類された、Web サイトのページ・アクセス統計です。図 5 に、この UI を示します。

図 5. 更新可能な棒グラフ・コンポーネント
react-bar-chart コンポーネントのスクリーンショット

左側の表内の各セルは編集可能です (セルまたはテキスト・フィールドをクリックして編集します)。編集すると、棒グラフが即時に更新されて、更新後の値が表示されます。

表 1 に、example3 のカスタム・コンポーネントを記載します。この表を参考に、example3 のソース・コードをご自分で調べてください。これらのカスタム・コンポーネントに付けられたラベルは、図 5 に明示されています。

表 1. 更新可能な棒グラフの例での React コンポーネント
React コンポーネント説明重要な状態/props
<MyTableCell>更新可能なテーブル・セル。制御された <input><button> と組み合わせています。セルの値が編集されていない場合、<input>は無効にされ、<button>{display: none} に設定されます。editing: セルが編集されているかどうかを追跡します。

data: セルに表示される値を常に反映します。
<MyTable>複数の行からなる表。各行に <MyTableCell> のインスタンスが含まれます。ステートレス。data は、各行に取り込むための textvalue を格納する props (hash の配列) です。onUpdate は、コールバック props で、編集セッションが終了すると起動され、変更されたセル情報を転送します。
<MyBarChart>コントローラー・ビュー。サード・パーティーの React コンポーネントを使用して<BarChart> のインスタンスをレンダリングします。Flux ストアとインターフェースを取って、サイト統計の変更時に通知を受け取ります。stats: textvalue を格納する hash の配列。グラフのデータを提供します。
<MyTableWrapper>コントローラー・ビュー。Flux ディスパッチャーとインターフェースを取り、テーブル内でセルの値が変更されるたびに、sitestats 更新アクションを送信します。sitestats: textvalue を格納する hash の配列。テーブル内の最新の値を追跡します。
<EditableChart><MyBarChart><MyTable> を組み合わせて最終的な UI にします。ステートレス。data は、初期データを提供する props です。初期データは Flux ストアから取得されて、組み合わされたコンポーネントに表示されます。

レンダリング対象の要素および物理 DOM にアクセスする

コードの大部分は仮想 DOM を使用しないと機能しませんが、実際のブラウザー DOM にアクセスしなければならない場合もあります。例えば、jQuery Mobile (「参考文献」を参照) や D3 などの既存のライブラリーを統合する必要がある場合や、<MyTableCell>_cellClicked() ハンドラーでも実際のブラウザー DOM にアクセスする必要があります。セルをクリックすると、そのセルは編集モードになり、実際の <input> ブラウザー DOM 要素にフォーカスがある状態になります。そのために使用するのは、getDOMNode() の遅延呼び出しです。

setTimeout(function() {
        this.refs.inp.getDOMNode().select();
      }.bind(this), 100);

refs は、仮想 DOM 内でレンダリング中に作成された要素にアクセスするために使用します (ブラウザー DOM の getElementById() と同様です)。

JSX 分散属性を使用してプロパティーを転送する

<MyTableWrapper> のすべての props は、ラップされた <MyTable> インスタンスに転送されますが、onUpdate の props 値については、このイベントの _dataUpdated コールバックを指すように変更しなければなりません。そのためには、以下のように JSX 分散属性の表記を使用します。

<MyTable {...this.props} onUpdate={this._dataUpdated}/>

プロパティーのデフォルト値

<EditableChart> では、sitestats という props の初期デフォルト値が Flux ストアから取得されます (このチュートリアルのセクション「Flux: React アプリケーションの拡張アーキテクチャー」を参照)。この初期デフォルト値を提供するには、getDefaultProps を使用します。React は、後で使用できるように値をキャッシュに入れます。

  getDefaultProps: function() {
      return {
        // initial default value only - cached by React
        sitestats: MockSiteStatsStore.getSiteStats()
      }
  }

React コンポーネントのスタイルを設定する

アプリケーションのスタイルを設定するには、通常の CSS を使用します。例えば、example3.css には、以下のようにアプリケーションのスタイルを設定する CSS3 flexbox コードが含まれています。

.containing{
    display: -webkit-flex;
    display: flex;
    width: 100%;
    height: 600px;
}

.left-side {
    width: 300px;
    -webkit-flex: none;
    flex: none;
}

.right-side {
    -webkit-flex: 1;
    flex: 1;
}

<EditableCell>className という props を使用して CSS クラスを指定していることに注意してください (これは、JSX キーワードの衝突を回避するためです)。

<div className="containing">
 <div className="left-side">
   <MyTableWrapper data={this.props.sitestats} />
 </div>
 <div className="right-side">
   ...
  </div>
</div>

コンポーネントの内部状態に応じて変わる可能性のあるスタイルを使用する場合は、JavaScript でインライン・スタイル設定を使用してコンポーネントのスタイルを設定することもできます。<MyTableCell> に、インライン JavaScript でスタイル設定されるネイティブ <button> が含まれています。

  <button onClick={this._okClicked} 
    style={this.state.editing? {zIndex: 5}: {display:'none'}}>
      ok</button>

Flux: React アプリケーションの拡張アーキテクチャー

Flux は、React コンポーネントを使用するアプリケーションを構造化する手法の 1 つです。Flux が規定する片方向のデータ・フロー・アーキテクチャーは、複雑な UI に見られるような相互に接続された MVC ネットワークに伴う問題を排除し、コード・ベースの長期的な保守性を向上させます。

簡単に言うと、コンポーネントのレンダリングの際に使われて共有される可変のアプリケーション状態は、アップストリームに送信されます。そして、上位レベルの所有者ビュー (example3 の <MyBarChart><MyTableWrapper> など) であるコントローラー・ビューが従来のコントローラーを置き換えます (この所有者ビューは、レンダリングされる状態をそのサブビューによって管理します)。この場合、従来のモデルの動作は、シングルトン・ディスパッチャーを介して対象のストアにアクションをルーティングすることによって行われます。これらのアクションは、実行する動作とその関連データを宣言するデータ・バンドルです (密結合されたメソッド呼び出しは、アクションによって疎結合されたデータ・フローに変換されます)。ストアとそれぞれの処理の相互依存性は、疎結合された方法でディスパッチャーを介して対処されます (デザイン・パターンの実践者は、この手法に Command and Chain of Responsibility パターンとの類似性を認めるかもしれません。システム・エンジニアは、マーシャリングおよびシリアライゼーションを連想することでしょう)。

直接的にでも間接的にでも、複数のコントローラー・ビューがアプリケーション状態を共有することは決してありません。コントローラー・ビューは対象とするデータ変更をストアに登録します。すると、変更が発生すると、ストアがビューにデータを取得するよう (そして自身で管理している状態を更新するよう) 通知します。これが、これらのビューに入る唯一のデータ・フローです。図 6 に、この片方向のデータ・フローを示します。

図 6. Flux での片方向のデータ・フロー
Flux での片方向のデータ・フローの図

example3 のコードはモック・ストアしか実装していませんが、その構造は、大まかに Flux パターンに従っています。<MyBarChart> は、あらゆるデータ更新に関して MockSiteStatsStore に登録します。<MyBarChart> で管理する状態は、このストアからの通知がない限り、変更されることはありません。

<MyTableWrapper> でインタラクティブに行われたデータ変更は、いずれも MockSiteStatsAction.update() 呼び出しにより、アクションとしてディスパッチャー/ストアに入ってきます。同じ UI 内に 2 つのコンポーネントが隣り合わせで存在するとしても、これらのコンポーネントが状態を直接共有することはありません。片方向のデータ・フローが、<MyTableWrapper> からディスパッチャーを介して対象の MockSiteStatsStore に入った後、変更通知が <MyBarChart> に返されます。

Flux アプリケーションのバインディング・パターンの詳しい説明は、この記事では行いません。詳細については、「参考文献」で調べてください。


React を使用した作業で使用できるツール

React Developer Tools は、Chrome ウェブストアから入手できる有用な Chrome ブラウザー拡張機能です。Chrome Devtools を使用してデバッグすると、難解なブラウザー DOM 表現ではなく、アプリケーションの React コンポーネント階層を表示することができます。React Developer Tools がインストールされている場合、example3 コンポーネント階層が図 7 のように表示されます。

図 7. コンポーネント階層を表示する React Developer Tools
React Developer Tools で表示されたコンポーネント階層のスクリーンショット

React コードのインスツメンテーション用アドオンとして、プロファイラーを使用することもできます。


React エコシステムの今後の展望

2015年 1月の React.js Conf で、Flux に対する Relay 機能拡張と React Native の両方が発表されました。

Relay

Relay は、Flux を拡張してサーバー・データの取得機能を組み込みます。Relay の背後にある重要な意図は、各 React コンポーネントがそれぞれ独立して固有のデータ取得要件を指定できるようにすることです。一般に、この場合のデータとは、コンポーネント自体をレンダリングする際に使用するデータを指します。Relay によって、コンポーネントのデータ要件を、UI ロジックが格納されているのと同じ (JSX) ファイル内で静的に宣言できるようになります。したがって、フルスタックの複雑なアプリケーションを扱う場合でも、一般的に起こりがちなファイルが取り散らかるという事態が大幅に軽減されます。さらに、開発中に JSX ファイルを編集した後にブラウザーを最新の表示に更新することによって、編集内容が正しいことを即座に検証し、同期させることも可能になります。

Relay を実現しているテクノロジーは、GraphQL です。GraphQL は、任意の形をしたデータを対象に構成可能な宣言型のクエリー指定言語です (Facebook ではすでに、この 2 ~ 3 年、本番で GraphQL を使用しています)。GraphQL を使用すれば、上位レベルの React コンポーネントが、(自らの要件の詳細に依存することなく) その所有するコンポーネントの要件を作成して、結合されたクエリーを作成してサーバーに送信することができます。

GraphQL クエリーによる結果は、汎用的な共通の Flux ストアに保管され、このストアに対象が登録されたビューに更新が通知されます。

React Native

React Native は、ブラウザー DOM を Android または iOS プラットフォーム用に置換します。これにより、React 開発者は、モバイル端末をターゲットとしたアプリケーションを開発できるようになります。

React Native の内部では、JavaScript コードが専用のシステム・スレッド上でネイティブ・インタープリターを使用して実行されます。いわゆるハイパフォーマンス非同期バッチ・ブリッジが、React コードをネイティブ・プラットフォームに接続し、ネイティブ UI コンポーネントを編成して UI を実装します。このブリッジを介して JavaScript にアクセスする、カスタムのネイティブ・メソッドを公開することもできます。React の HTML 要素は、モバイル・プラットフォーム上でネイティブ・コードのビューと UI ウィジェットに置き換えられます。React Native は、JavaScript での CSS のサブセットを使用したネイティブ・コンポーネントのスタイル設定をサポートします。

開発チームによると、React Native を使用して作成したアプリケーションでは、ユーザーがネイティブ・アプリケーションのみを連想するような、繊細で微妙な UI のニュアンスを再現することができます。これは、WebView や HTML5 ベースのアプリケーションでは現在対応できないことです。


まとめ

JavaScript 開発者はプロジェクトの UI を作成する際に、多種多様なオープンソースのフレームワークやライブラリーを自由に選ぶことができますが、その豊富な選択肢のうち、パフォーマンスに影響しやすい複雑な UI プロジェクトで試されたのは、ほんのわずかです。高度なツール・サポートがあるもの、コーディングとデザインのベスト・プラクティスとして実証されたもの、そしてサーバー・サイドのデータ操作およびモバイル開発にまで拡張するという将来の成長の道筋が約束されているものとなると、さらにその数は減ります。拡大し続け、活気に満ちたコミュニティーでサポートされている React は、このすべてを上回る条件を満たします。React を皆さんの開発ツールボックスに追加するには、今がまさに絶好のタイミングです。


ダウンロード

内容ファイル名サイズ
Source code for this article's exampleswa-react-intro.zip7.97KB

参考文献

学ぶために

  • React: React の公式 Web サイトにアクセスして最新版を入手し、参考になるドキュメントを読んでください。
  • 「React を使用して IBM Watson エクスプローラーを作成する」: Sing Li によるこの関連チュートリアルで React を実際に使用してください。
  • Flux: React のコンポーネントとプラグアンドプレイする拡張アプリケーション設計アーキテクチャーとして、Flux (そして程なく Relay) の詳細を調べてください。
  • 「Tooling Integration」: 本番デプロイメント環境で最高のパフォーマンスを発揮するために、あらかじめコンパイル済みの JSX を使用する方法を参照してください。
  • SyntheticEvent: React の SyntheticEvent 実装について詳しく学ぶとともに、React コンポーネントを通じてどのようにイベントが伝播するかを参照してください。
  • jquery-bootstrap: 既存のプロジェクトへ React を統合する作業を始めてください。React チームのこのサンプルには、React を jQuery-Bootstrap プロジェクトに追加する方法が示されています。すでに jQuery Mobile UI を使用している場合は、このサンプルによって React のサポートが追加されます。this
  • 「React Server Rendering Example」: React のハイパフォーマンス仮想 DOM を利用して、サーバー・サイドでサイトをレンダリングしてください。この手法は、React コンポーネント・テクノロジーを採用する一方で静的サイトの SEO のすべてのメリットを維持します。この PHP ベースのサーバー・サイドのサンプルにその方法が示されています。
  • React プロファイラー: プロファイラーのアドオンを使用して、React コードのパフォーマンスをプロファイリングしてください。
  • React.js Conf の動画: React エコシステムにおける Relay、React Native、その他の関連テクノロジーを導入する React チームによる動画を見てください。

製品や技術を入手するために

  • React: このプロジェクトのオープンソース GitHub リポジトリーから React ソース・コードを入手してください。
  • 著者のリポジトリーからこのチュートリアルのサンプル・コードの最新の更新を入手してください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development, Open source
ArticleID=1006824
ArticleTitle=React: 保守しやすいハイパフォーマンスの UI コンポーネントを作成する
publish-date=06042015