galapagosit’s blog

本家 > http://galapagosit.com/

fluxibleのサンプル(チャット)を読む

やること

Fluxible | A Pluggable Container for Isomorphic Flux Applications

というものがある。
今流行りのreact.jsを乗せたfluxアーキテクチャの実装の中では、割とオススメされているっぽいものだ。

これの習得をせまられる状況になったのだが、はっきり言ってチンプンカンプンである。そもそもnode自体の理解もおぼつかないのに、こんなこと人間にはできません。

とはいえ、下記のサンプル(chatの方)を読んで気合いで理解を進めた話。 github.com (commit:967b623e9411df46383e0ebe0adedec48e57d511)

app.js

flux-examples/app.js at master · yahoo/flux-examples · GitHub


var app = new Fluxible({
    component: require('./components/ChatApp.jsx')
});

まずはここから。
これがアプリケーションそのもので、これに対していろいろ設定を加えて行ってるっぽい。
引数のcomponentは
「Stores your top level React component for access using getComponent()
ってソースには書いてあるんだけど、だとしたら"components/Html.jsx"がトップレベルなんじゃないの? 分からないので飛ばします。


app.registerStore(require('./stores/RouteStore'));
app.registerStore(require('./stores/MessageStore'));
app.registerStore(require('./stores/ThreadStore'));
app.registerStore(require('./stores/UnreadThreadStore'));

ここでは、データを溜め込むためのStoreをアプリケーション登録している。

server.js

flux-examples/server.js at master · yahoo/flux-examples · GitHub

サーバサイドでブラウザからのアクセスを待ち受ける為、最初に動くコードである。


require('babel/register')

このアプリに限ったことではないが、コードをES6で記述できるようにするものらしい。


var server = express();

pythonにおけるflask、rubyにおけるsinatra的なやつである。


// Get access to the fetchr plugin instance
var fetchrPlugin = app.getPlugin('FetchrPlugin');
// Register our messages REST service
fetchrPlugin.registerService(require('./services/message'));
// Set up the fetchr middleware
server.use(fetchrPlugin.getXhrPath(), fetchrPlugin.getMiddleware());

これはブラウザからサーバのコードを叩きたい時に、簡単に叩く為の入り口を設定してあげる機能と理解した。
以下のように書くと、サーバ側の処理を以下の様に呼び出せるようになる。

context.service.somefunc('message', message, {} ...

    var context = app.createContext({
        req: req, // The fetchr plugin depends on this
        xhrContext: {
            _csrf: req.csrfToken() // Make sure all XHR requests have the CSRF token
        }
    })

これがリクエストに紐づくコンテキスト。プラグインなんかを使うとこいつに対して便利なメソッドが生えるとか、生えないとか。


    debug('Executing showChat action');
    context.executeAction(navigateAction, { url: req.url, type: 'pageload' }, function (err) {

ここでアクションを実行し、結果的に必要な情報をStoreに登録している。


        debug('Exposing context state');
        var exposed = 'window.App=' + serialize(app.dehydrate(context)) + ';';

        debug('Rendering Application component into html');
        var html = React.renderToStaticMarkup(HtmlComponent({
            state: exposed,
            markup: React.renderToString(context.createElement())
        }));

        debug('Sending markup');
        res.send(html);
  • exposedの中身をconsole.log gist.github.com contextに紐づくデータがjson形式でシリアライズされている様だ。
    スレッド一覧やチャットの発言のデータも含まれる。

  • htmlの中身をconsole.log gist.github.com exposedの内容を含むhtml全体が入っている。
    data-reactid=".1dbtrevj3ls.0.1" みたいなのが、タグの属性に入ってるのがミソなんだろう、きっと。

client.js

flux-examples/client.js at master · yahoo/flux-examples · GitHub

こちらは、上記のサーバにアクセスしたのち、ブラウザ側で最初に実行されるコード。


bootstrapDebug('rehydrating app');
app.rehydrate(dehydratedState, function (err, context) {

dehydratedStateはサーバ側で格納されていたexposedの事である、app.rehydrateする事でcontextに復元を行っている。


    var mountNode = document.getElementById('app');

    bootstrapDebug('React Rendering');
    React.render(context.createElement(), mountNode, function () {
        bootstrapDebug('React Rendered');
    });

mountNodeっていうのが、app.jsで指定していた'./components/ChatApp.jsx'の中身になっているっぽい。
context.createElement()
というのは、「React.createElement」メソッドの結果が返るみたい。