Web Developer's Struggle Memories

日々の業務から思ったこと、学んだことを書き連ねていきます。

「What the flux?」の翻訳

本投稿は以下の記事を日本語に翻訳したものになります。
(筆者の@jcreamer898氏に許可は頂いてます)

jonathancreamer.com

この記事は、Fluxについて物凄く分かりやすく書かれているので、「Flux難しい!」って言う方の助けになれば幸いです(*^^)v

以下、本文です。 ↓↓↓

What the flux?

私はこの数週間、私の脳をReact.jsとFluxアーキテクチャーでぐるっと包み込むことに没頭してきました。

嘘をつくつもりはありませんが、私はそれを避けていました。FluxやReactについてのドキュメントやブログ記事を見ると、私の脳は…

http://c2.thejournal.ie/media/2014/10/mark-13.gif

componentDidMountDispatchersActionCreatorsなど色んなものが、私が中に踏み入ろうと試みたときに、私を怖がらせました。それはちょうどコンピュータのようなものでした。数日経ちましたが、今では私はこれが好きです。本当に。

Fluxで私の頭をしっかりと包み込むのに数日かかりました。特にこの画像。

http://d.pr/i/1gDAc+

私はついにそれを取り巻くもので私の頭を包み込むようになりましたが、他の人がもう少し早くFluxを理解するのを手助けするために、私自身のバージョンを考え出すことにしました。

http://d.pr/i/1inWl+

ではこの全てを解体してみましょう…

Views

Show some stuff.

ビューはコンポーネントレンダリングされるものです。あなたのコンポーネントにはいくつかの役割をがありますが、例えば次のようなものがあります。

var NewEmailComponent = React.createClass({  
    getInitialState() {
        return {
            to: ""
        };
    },
    render() {
      return (
              <input name="to" type="email" onChange={this.updateToText} />
              <button onClick={this.createNewEmail}>New Email</button>
          );
    },
    updateToText(event) {
        this.setState({
            to: event.target.value 
        });
    },
    createNewEmail(event) {
        var to = this.state.to;
        EmailActionCreator.createNewEmail(to);
    }
});

上記から分かるように、Reactのすべてのコンポーネントには状態があります。私たちのNewEmailComponentは、まず初めに to:""という状態を持ちます。

次に、renderは表示するJSXを返します。ここでは入力とボタンがあります。

onChange = {this.updateToText}onClick = {this.createNewEmail}に気づいたでしょう。これらの両方が、イベントハンドラコンポーネントに追加し、それぞれの関数を呼び出しています。

toの入力テキストが変更されると、toの変更内容で状態が変更されます。

Action Creators

Go get some more stuff

アクション作成者は"アクション"を作成します。はい。それと、必要に応じてサーバーからデータを取得するのが一般的です。 だから、少しの間ビューに戻ってみましょう…

ボタンをクリックすると、EmailActionCreator.createNewEmail(to)関数がtoに渡すデータの種類に関係なく発火します。 ID、名前、何でもかまいません。今回の例ですと、送信する電子メールになります。

EmailActionCreator.createNewEmail関数はいくつかのことを行います。

  1. 新しい電子メールを作成するアクションを作成
  2. APIをコールして下書きを作成

Actions

Do something with stuff

アクションはJavaScript POJOs(plain old javascript objects)のことです。 大抵、慣例によってtypeといくつかのdataを持っています。 どのように、そしてなぜそれらが作られたのか、まず初めに一歩引いて考えてみましょう。

さて、私たちのアクション作成者は “action"を作成します。 一般的には、可能な限りの異なるアクションタイプのオブジェクトを、すべて格納するファイルがあります。

// actions/fooActions.js
var keyMirror = require('keymirror');

module.exports = keyMirror({  
    CREATE_EMAIL: null,
    EMAIL_CREATED: null,
    SEND_EMAIL: null
});

あなたはこれらのアクションを使用して、UIまたはサーバーからトリガーできるものを定義します。

今回の例でkeyMirrornullの値を作成するために使用され、大文字のキー名はミラーリングされます。 いわゆる、ショートカットのライブラリです。

では、EmailActionCreatorに戻って、これらの異なるアクションタイプを使ってみましょう。

// actions/emailActionCreator.js

module.exports = {  
    createNewEmail(to) {
        EmailAppDispatcher.handleViewAction({
            type: ActionTypes.CREATE_EMAIL,
            to: to
        });

        EmailWebService.createEmail({
          to: to
        });
    },
    emailCreated(mail) {
        EmailAppDispatcher.handleServerAction({
            type: ActionTypes.EMAIL_CREATED,
            mail: mail
        });
    }
}

EmailWebServiceはあなたのAPIをたたくことができますが、あなたが望むのなら…

// data/emailWebService.js
module.exports = {  
    createEmail: function(options) {
        $.ajax({
            url: "/api/email",
            type: "POST"
        }).done(function(id) {
            var mail = { 
                to: options.to, 
                id: id 
            };

            EmailActionCreator.emailCreated(mail);
        });
    };
};

ここでは、jQuery.ajaxを使用して新しい電子メールを作成しています。 電子メールの作成が完了すると、電子メールが作成された全員に伝えるために、もう一つ別のアクションがEmailActionCreator.emailCreated(mail);と共に作成されます。このアクションのタイプはActionTypes.EMAIL_CREATEDです。

次にディスパッチャーと、そのhandleViewActionおよびhandleServerAction要素について話します。

Dispatcher

Hey Everyone! There’s new stuff!

ディスパッチャの唯一の仕事は、何らかのメッセージ、またはイベント、あるいはあなたが呼び出したいものを公開して、 変更を検知しようとしている人に何かが起こったことを知らせることです。

例えば、誰かが最初にCreate Emailボタンをクリックしたときに、EmailActionCreatorによって1つのアクションが作成され、 ディスパッチャ上のhandleViewActionメソッドを介してディスパッチャに送信されます。

ディスパッチャは一度生成されるとまったく変更する必要のない定型コードです…

var EmailAppDispatcher = assign(new Dispatcher(), {  
  handleServerAction(action) {
    var payload = {
      source: PayloadSources.SERVER_ACTION,
      action: action
    };
    this.dispatch(payload);
  },
  handleViewAction(action) {
    var payload = {
      source: PayloadSources.VIEW_ACTION,
      action: action
    };
    this.dispatch(payload);
  }
});

module.exports = EmailAppDispatcher;  

Flux.jsには他のいくつかの間(*1)で、registerdispatch機能を持つデフォルトのディスパッチャが備わっています。

私たちのEmailAppDispatcherには、handleViewActionhandleServerActionという2つのヘルパー関数があります。 この2つの異なる関数は絶対に必要なものではありませんが、もし必要になった場合、アクションがどこから来たのかを正確に知ることができます。 とは言うものの、Fluxのdefault Dispatcherから組み込みのdispatch関数を呼び出すだけです。

アプリ内の他の場所では、次に話しますが任意のストアも、ディスパッチャのregister関数を使うと、アクションが送信されていることが通知されます。

Store

Keep track of stuff

ストアはアプリケーションの状態とビジネスロジックを保持します。 また彼ら自身も一般的にはイベントエミッタです。 これは、あなたはストアが公開したイベントを受け取ることができる、と言うことを意味します。 したがって、ビューはストアの変更イベントにバインドされ、異なるストアメソッドを呼び出してデータを取得し、新しいデータで状態を更新します。

理想的には、ストアがajaxリクエストを行わないことです。なぜなら、アクションはデータを送信する必要があり、ストアはそれを管理するだけなので。

ストアは通常、ある種のデータ / 状態を保持するローカル変数を持ちます。つまり、以下の場合では、単なるアクティブな電子メールになります。

var EventEmitter = require("events"),  
    assign = require("react/lib/assign"),
    Email = require("./core/email"),
    _ = require("underscore");

var CHANGE_EVENT = "change";

var activeEmail = null;

function createNewEmail(mail) {  
    var email = new Email(mail);

    activeEmail = email;
}

var EmailStore = assign({}, EventEmitter.prototype, {  
  emitChange() {
      this.emit(CHANGE_EVENT);
  },
  addChangeListener(callback) {
      this.on(CHANGE_EVENT, callback);
  },
  removeChangeListener(callback) {
      this.removeListener(CHANGE_EVENT, callback);
  },
  getActiveEmail() {
      return activeEmail;
  }
});

emitChangeaddChangeListener、およびremoveChangeListenerは各ストアで目にする関数であり、かつ基になるEventEmitterのメソッドemitonそしてremoveListenerの抽象クラスです。 CHANGE_EVENTは変更イベントの名前の文字列を保存するだけです。

getActiveEmailメソッドはアクティブな電子メールを返します。

ディスパッチャに再度バックアップし、register関数を公開することを覚えておいてください。 上記のようにストアのためのメソッドを定義した後、私たちはディスパッチャに「登録」します…

EmailStore.dispatchToken = EmailAppDispatcher.register((payload) => {  
    var action = payload.action;

    switch (action.type) {
        case ActionTypes.EMAIL_CREATED:
            createNewEmail(action.mail);
            EmailStore.emitChange();
            break;

    }
});

ディスパッチャが任意のアクションを送信するたびに、register関数がコールされます。 ただし、switch文に型が含まれている場合にのみ注意します。今回のケースではActionTypes.EMAIL_CREATEDだけです。

ディスパッチャから送信されたアクションがActionTypes.EMAIL_CREATEDである場合、単にアクティブな電子メールを設定するだけのcreateNewEmail関数をコールしてレスポンスを返します。 この時、変更イベントを発行するようにストアに指示します。

一連の流れに戻り、もう一度やり直してみましょう…

var EmailStore = require("../stores/emailStore");

/** ... *./
{
    componentWillMount() {
        EmailStore.addChangeListener(this._onChange);
    },
    _onChange() {
        this.setState({
            activeEmail: EmailStore.getActiveEmail()
        });
    }
}

componentWillMount関数はコンポーネントの準備が整うと発火します。 componentWillMountはストアに変更リスナーを追加することができる変更イベントです。 ストアが変更されるたびに、this._onChangeがコールされます。

_onChange関数にて作成された新しい電子メールでコンポーネントの状態を設定できます!

ドン。

TL;DR(要約)

View Renders ->
Click triggers action creator ->
Go get data ->
Create action for recieved data ->
Send action to dispatcher ->
Dispacther publishes action ->
Store listens for action ->
Store updates state ->
View updates

さて、今あなたの心はどちらかでしょう…

http://i.imgur.com/16tx1Ft.gif+

もしくは…

http://i.imgur.com/tfyHIaB.gif+

もしあなたがテーブルをひっくり返したい場合は、この記事をもう一度読んで、これらの例を見て、やり直してみてください。 あなたはそれ(Flux)を手に入れ、そしてあなたは愛するでしょう。

終わりに

翻訳記事を投稿する許可を下さいました@jcreamer898氏、ありがとうございました!! 改めて御礼申し上げます。

さて、誰かの英語記事を丸々翻訳して投稿するのは今回が初めてでしたが、物凄い勉強になりました。 また、自分の日本語が下手すぎることも痛感しました。色んな本読んで、表現力を身に着けたいものです。

冒頭にも書きましたが、この記事がこれからFluxを学ぼうとしている方の理解を助けることができれば幸いです。では。

*1:ここだけ意味がわかりませんでした。原文は「amoung a few others」