在Flux中,负责与API直接交谈

时间:2015-02-06 11:39:04

标签: javascript facebook reactjs reactjs-flux

我正在努力学习Flux,并且已经观看并阅读了这些令人惊叹的资源

我仍然不明白Flux架构(ActionDispatcherStore)的哪一部分负责与API交谈,前提是我的API是异步,并且能够推送数据 - 即当新数据可用时我得到一个事件。

此图片显示Action正在与API通信,但多个代码示例显示Action仅触发Dispatcher .. Flux architecture

2 个答案:

答案 0 :(得分:2)

如果您将Actions的角色视为通知Stores更新的状态数据,那么在调用Action之前(例如,在组件的事件处理程序中),实际获取新数据的API调用应该是明智的。但是,您可能不希望在整个视图中分散与API相关的逻辑。为避免这种情况,有时会在上图中的View和Action之间引入ActionCreators模块。

通过调用适当的Actions来进行API调用和处理返回数据的方法可以在ActionCreators中收集,因此它们将松散地耦合到您的视图。例如,

user clicks login ->
click handler calls ActionCreator.login(), which makes the API call ->
result is passed to Stores by calling Actions ->
Stores update their state accordingly

如果您的服务器可以通过诸如websockets之类的东西推送更新,则相应的事件侦听器也可以调用ActionCreators中定义的方法,因此您的所有操作都从一个地方发出。或者,您可以将用户启动的ActionCreators和服务器启动的ActionCreators拆分为单独的模块。无论哪种方式,我认为这实现了良好的关注分离。

答案 1 :(得分:2)

在使用React + Flux几个月后,我遇到了同样的问题并尝试了一些不同的方法。 我得出的结论是,最好的办法是让行动处理远程和本地的数据更新:

# COMPONENT
TodoItems = React.createClass
    componentDidMount: ->
        TodoStore.addListener("CHANGE", @_onChange)
    _onChange: ->
        @setState {
            todos: TodoStore.get()

    _onKeyDown: (event) ->
        if event.keyCode == ENTER_KEY_CODE
            content = event.target.value.trim()
            TodoActions.add(content)

    render: ->
        React.DOM.textarea {onKeyDown: @_onKeyDown}


# ACTIONS
class TodoActions
    @add: (content) ->
        Dispatcher.handleAction({type: "OPTIMISTIC_TODO_ADD", todo: {content: content}})
        APICall.addTodo({content: content})

# STORE
class TodoStore extends EventEmitter
    constructor: ->
        @todos = [] # this is a nice way of retrieving from localStore
        @dispatchToken = @registerToDispatcher()

    get: ->
        return @todos

    registerToDispatcher: ->
        Dispatcher.register (payload) =>
            type = payload.type
            todo = payload.todo
            response = payload.response

            switch type
                when "OPTIMISTIC_TODO_ADD"
                    @todos.push(todo)
                    @emit("CHANGE")

                when "TODO_ADD"
                    # act according to server response
                    @emit("CHANGE") # or whatever you like


#### APICall
class APICall # what can be called an 'action creator'
    @addTodo: (todo) ->
        response = http.post(todo) # I guess you get the idea
        Dispatcher.handleAction({type: "TODO_ADD", response: response})

如您所见," 果汁"在TodoActions之内。添加待办事项后,TodoActions.add()可以通过OPTIMISTIC_TODO_ADD触发乐观的UI更新,该更新将插入TodoStore.todos。并行地知道这必须传达给服务器。 外部实体 - ApiCall(可被视为动作创建者) - 负责处理此操作的远程部分,当您收到响应时,它遵循其正常路线TodoStore可以采取行动相应

如果您让商店直接负责远程内容管理,您将为其添加额外的复杂层,这使我对某个时刻的数据状态缺乏信心。

让我们想象一下:

class TodoActions
    # TodoActions is `dumb`, only passes data and action types to Dispatcher
    @add: (content) ->
        Dispatcher.handleAction({type: "TODO_ADD", todo: {content: content}})
        # APICall.addTodo({content: content})

class TodoStore extends EventEmitter
    # ...
    registerToDispatcher: ->
        # ...
        when "TODO_ADD"
            @todos.push(todo)
            # now the store has to push it to the server
            # which means that it will have to call actions or the API directly = BAD
            # lest assume:
            APICall.addTodo({content: content})

            # it also generates some uncertainty about the nature of the event emit:
            # this change can guarantee that data was persisted within the server.
            @emit("CHANGE")

我首先提供的解决方案提供了一种很好的方式,可以对UI进行乐观更新,处理错误并显示加载指示,直到我经历过。