Javscriptのクラス対応(=React 0.13対応)のFlummoxを試してみた

前回の記事で快適なReact環境を手に入れることが出来た私ですが、Fluxを導入しようとして壁にぶち当たりました。

Fluxxorなどの有名なFlux実装などは StoreとComponentの紐付けに従来のMixinを利用することが前提となっていますが、ReactにおいてMixinはReact.Componentを利用していると使えません。

ちょっとGithubを見ていたらacdlite/flummoxが特徴にReact 0.13対応だと書いてあったのでとりあえずTodoを実装しました。

簡単な説明

Actions

class TodoActions extends Actions
  createTodo: (text) ->
    id = (+new Date() + Math.floor(Math.random() * 999999)).toString(36)
    {
      id: id
      text: text
      complete: false
    }

関数の返り値が自動的にDispatcherに送られる。(undefinedを送るとDispatcherに無視されるので注意)

Store

class TodoStore extends Store
  constructor: (flux) ->
    super
    todoActions = flux.getActionIds('todo')
    @register(todoActions.createTodo, @handleNewTodo)
    @state = {
      todos: {
        1: {
          id: 1
          complete: true
          text: "hoge"
        }
      }
    }

  handleNewTodo: (todo) =>
    todos = @state.todos
    todos[todo.id] = todo
    @setState todos: todos

React.Componentっぽく書ける。setState()すると自動的にDispatcherにemitされ紐付いたComponentが更新される。

@register(actionId, handler)でDispatcherにhandlerが登録される。 ActionsIdが示すアクションの返り値がDispatcherを介してhandlerの引数となりhandlerが実行される。

handlerは関数だったらなんでもいいらしいが、普通にメソッドを渡すといい。

Flux

class AppFlux extends Flux
  constructor: ->
    super
    @createActions('todo', TodoActions)
    @createStore('todo', TodoStore, @)

createActions(key, ActionsClass, …args)とcreateStore(key, StoreClass, …args)でActionsとStoreが登録できる。argsはそれぞれcreateするときの 引数になる(この場合Storeの方はnew TodoStore(this)となる)

このインスタンスのメソッドgetActions(key)やgetStore(key)を呼び出すとActionsやStoreのインスタンスが返ってくる。

Component

あとはflux = new AppFlux()してComponentを書いていく。

React.render(React.createFactory(Application)(flux: flux), document.getElementById('container'))
class Application extends React.Component
  render: =>
    jade.compile("""
      FluxComponent(flux=flux connectToStores=['todo'])
        TodoPanel
    """)(_.assign(@, @props, @state))

FluxContainer flux=flux connectToStores=[key]とするとStoreの@stateがFluxComponentの@propsになる。だから、中のComponentから参照できる。

(この理解が微妙に適当。親のpropsをなんで参照できるのか分かってないまま書いた)

FluxComponentは別に配列じゃなくてもいい。

FluxComponent connectToStores={{
  posts: store => ({
    post: store.getPost(this.props.post.id),
  }),
  comments: store => ({
    comments: store.getCommentsForPost(this.props.post.id),
  })
}}

Actionを呼び出すときは@props.flux.getActions("todo").createTodo(@state.newTodoText)といった感じ。

所感

こんな感じにFLux書きたかった。

Facebookのfluxは初見の私には魔法の言葉が飛び交っているようにしか見えなかったです。

参考