読者です 読者をやめる 読者になる 読者になる

野次馬エンジニア道

野次馬な気持ちでプログラミングをあれこれと綴ります

React入門 - コンポーネント・サーバサイドレンダリング・Flux

React JavaScript

年末年始は本屋でオライリーのカレンダーをもらうのが毎年恒例。カレンダー欲しさに買った

入門 React ―コンポーネントベースのWebフロントエンド開発

入門 React ―コンポーネントベースのWebフロントエンド開発

最近周囲のプロジェクトでも採用されることが徐々に増えているのでサッと目を通した。

コンポーネントの合成

Reactとはどのようなものか。ReactのコンポーネントはHTMLの要素にJavaScriptの表現力が追加されたようなイメージ。6章のコンポーネントの合成のP47の文章を借りると、「コンポーネントはpropsとstateを入力として受け取り、HTMLを出力として返す一種の関数のようなもの」となる。また考え方として

P47より

Reactは「継承(inheritance)」よりも「合成(composition)」を好みます。小さくて単純なコンポーネントを組み合わせて、大きくて複雑なアプリケーションを構築するのです。他のMVCフレームワークオブジェクト指向のツールに慣れた方は、React.extendClassのような関数を期待するかもしれませんが、そのようなものは存在しません。

propsとstate

propsはプロパティのことでコンポーネントに渡されるデータ。作成時に属性値として渡されてthis.propsで参照可能。あくまで参照用。一方のstateはコンポーネントが持つ内部状態のこと。コンポーネントの内部でのみ利用される。必ずsetStateで更新をかける。するとコンポーネントrenderが呼ばれる仕組み。

JSX

コンポーネントの記述にはJSXが使える。JSXは「JavaScript XML」の略。React内のコンポーネントでHTMLのようにXMLで宣言的に記述できることで構造化が容易になる。ビルドツールもしくはライブラリを使ってオンデマンドにJavaScriptに変換可能。

React.render(
    <h1>Hello, world!</h1>, document.body
    // React.createElement("h1", null, "Hello, world!"), document.body に変換
);

ref属性

JSXの中の特別な属性の一つ。コンポーネントのthisコンテキストから別のコンポーネントを参照するためのReact独自の属性。親のコンポーネントから子のコンポーネントを参照可能。次の例だと、this.refs.myInputのようにアクセスする。

render : function() {
    return <div> 
        <input ref="myInput" />
    </div>;
}

P19より

ref経由でアクセスできるオブジェクトは「バッキングインスタンス」と呼ばれて実際のDOMではなく、コンポーネントの情報が記述されたもので、Reactは必要であればこの記述から実際のDOMを作成します。実際のDOMにはReact.findDOMNode(this.refs.myInput)のようにアクセスします。

React.findDOMNodeを使えば実際のDOMにアクセスできるが、renderのメソッドの中ではDOMノードにアクセスできない。初回の描画が成功して実際のDOMが表示されたタイミングで呼ばれるcomponentDidMountの中でReact.findDOMNode経由でDOMにアクセス可能となる。

key属性

これもJSXの中の特別な属性の一つ。Reactの中身がどのように動作しているのか垣間見える。11章のパフォーマンスチューニングの記述を引用する。

P105より

key属性は不要な処理をさけるために使用されます。例えば、<div key="foo">のように定義されたコンポーネントがあったとして、key属性の値が後ほど"bar"に変更された場合、Reactは差分計算を行わずに、即座に子コンポーネントも含めた全ての仮想DOMオブジェクトを破棄し、一から描画し直します。

リストなどを使っていて全体を再描画する場合は無駄な計算をスキップすることができる。一方で順番が変わるだけ場合すでにその要素はDOMツリーの中に存在するので、

P106より

return <img src={item.src} key={item.id}/>; これにより、Reactはkey属性をもとにリストの要素の順番だけが変更されたことを知ることができるので、img要素のsrc属性を設定する代わりに insertBeforeを実行して、単純にDOMノードを移動します。

のようにチューニング可能。

サーバーサイドレンダリング

シングルページアプリケーションを構築している人が次に気になるのは12章のサーバーサイドレンダリング

SEOに関しては先日 AJAX crawlingが非推奨になったこともあり*1それが動機となることはないかもしれないが、冒頭にある

P109より

シングルページアプリケーションはクライアントサイドでHTMLページを作成するので、検索エンジンのインデックス対象になりません。 また、JavaScriptのロードが完了してからページを作成するので、初回のページ表示が遅いという問題があります。 サーバーサイドレンダリングはこれらの問題に対する有効な解決策となりえます。

というのは非常に大きい利点。それも仮想DOMのなせる技といえる。

Reactのコンポーネントは、実際のDOMに反映する前にメモリー上のデータとして仮想DOMを出力する。それを単純にHTML文字列として出力しサーバクライアントで共有することができる。サーバーサイドではターゲットのDOMを引数に設定しないReact.renderToStringを実行する。

data-react-checksum

renderToStringをするとReactは二つの属性を追加する。

<div data-reactid=".fgvrzhg2yo" data-react-checksum="-1663559667">Hello, world!</div>

data-reactidはDOMノードを特定するために利用されてstateやpropsに応じて変化する。data-react-checksumはサーバサイドのレンダリング時にのみ追加される。サーバサイドで作成されたDOMが再利用可能かチェックするための値。

ライフサイクル

renderよりも後のコンポーネントのライフサイクルのメソッド(componentDidMount, componentWillUnmount)はサーバ側では呼ばれない。なのでイベントリスナーをcomponentWillMountで登録せずにcomponentDidMount, componentWillUnmountで登録削除を行うのがベストプラクティス。

Flux

アーキテクチャパターン。単一のデータフローを強いることによってアプリケーションの動作が推測しやすい利点がある。

P200より

Fluxは3つの主要なパーツ - Store, Dispatcher、View (Reactのコンポーネントツリー) - で構成されています。さらにActionを4つの目の主要なパーツとみなすことができます。Actionに補助的なメソッドを提供することで、Dispatcherへのインターフェースとして機能します。

Fluxにおいて、最上位のReactコンポーネントはController-Viewとしての役割を担います。

書籍の図は日本語訳にへ変更されているがイマイチピンとこない。のでオリジナルの図*2を見てみる。

https://raw.githubusercontent.com/facebook/flux/master/docs/img/flux-diagram-white-background.png

Action・Dispatcher

ユーザがUIに対して行った操作はAction経由でDispatcherへ。

全てのデータは必ずDispatcherを通ることに注意。Dispatcherシングルトンオブジェクトになっている。Storeへ通知と依存の管理はDispatcherが行う。実際のアプリでは複数のStoreを扱うユースケースもありえる。その際のアイデアとしては、

P206より

  • DispatcherはActionをキューに格納するように変更する
  • DispatcherはActionの処理が完了するまで処理を停止できるように変更する
  • Dispatcherにコールバックを登録する際に、どのActionに対して待ち受けるか指定できるようにする

Actionは言わばDSLの役割を果たしていることになる。ユーザのUIなどの操作をStoreが意味として理解できるように変換する。「補助的に」という説明はここからきている。

Store

データのやり取りは全てここに集約しここにビジネスロジックもここに入れる。その際

P204より

  • Storeはアプリケーションのすべてのデータを保有する
  • アプリケーションの他の部分はデータの操作方法を知らないしたがって、Storeはアプリケーションで唯一データの変更が行われる場所である
  • Storeはgetterメソッド(値を取得するためのメソッド)のみサポートし、setterメソッド(値を変更するためのメソッド)を持たない。データの変更はすべてDispatcherのコールバック経由で行われる

に注意する。データとそれに関連する操作を一緒に持つのがポイント。さらに図の通りchangeイベントが発行されてそれがController-View経由で親から子の一方向で処理されていく。

まとめ

単純な入門書というよりは、基本となるReactの思想から実践的な設計やテスト手法まで網羅されている充実の内容だった。実務で使うにはもう少し利用例や組み合わせて使うライブラリなどの良し悪しを検討する必要がありそう。