套接字在哪里适合 Flux 单向数据流?

Where do sockets fit into the Flux unidirectional data flow?

套接字适合Flux单向数据流的什么地方?关于远程数据应从何处进入 Flux 单向数据流,我阅读了两种观点。我看到 Flux 应用程序获取远程数据的方式是在进行服务器端调用时,例如,在随后被解决或拒绝的承诺中。在此过程中可能会触发三种可能的操作:

  1. 乐观更新视图的初始操作(FooActions.BAR)
  2. 解决异步承诺时的成功操作(FooActions.BAR_SUCCESS)
  3. 拒绝异步承诺时的错误操作(FooActions.BAR_ERROR)

商店将侦听操作并更新必要的数据。我已经看到动作创建者和商店本身发出的服务器端调用。我在上述过程中使用了 action creators,但我不确定是否应该对通过 web 套接字获取的数据进行类似处理。我想知道套接字在下图中的位置。

将 Flux 与 WebSockets 或普通的旧 HTTP requests/polling 结合使用的方式确实没有区别。您的商店负责在应用程序状态更改时发出更改事件,如果该更改来自 UI 交互、WebSocket 或创建 HTTP,则从商店外部不应该看到该事件要求。这确实是 Flux 的主要优势之一,因为无论应用程序状态在何处更改,它都会通过相同的代码路径。

一些 Flux 实现倾向于使用 actions/action creators 来获取数据,但我不太同意。

操作是修改应用程序状态的事情。类似于 "the user changed some text and hit save" 或 "the user deleted an item"。将操作想象成数据库的事务日志。如果你丢失了你的数据库,但是你保存并序列化了所有曾经发生的动作,你可以重放所有这些动作并以你丢失的相同 state/database 结束。

所以 "give me item with id X" 和 "give me all the items" 之类的东西不是操作,它们是问题,是关于应用程序状态的问题。在我看来,商店应该通过您在这些商店中公开的方法来回答这些问题。

很想使用 actions/action creators 来获取,因为获取需要异步。通过将异步内容包装在操作中,您的组件和商店可以完全同步。但如果你这样做,你就模糊了动作的定义,而且它还迫使你假设你可以将整个应用程序状态放入内存(因为只有在内存中有答案时你才能同步响应)。

下面是我对 Flux 和不同概念的看法。

商店

这显然是您的应用程序状态所在的位置。 store 封装和管理状态,并且是该状态实际发生变化的唯一地方。当状态发生变化时,它也是发出事件的地方。

商店还负责与后端通信。当状态发生变化并且需要与服务器同步时,商店与后端通信,当它需要内存中没有的数据时,它也会与服务器通信。它有 get(id)search(parameters) 等方法。这些方法是针对您的问题的,它们都是 return 承诺,即使状态可以装入内存。这很重要,因为您最终可能会遇到状态不再适合内存,或者无法在内存中过滤或进行高级搜索的用例。通过 return 从您的问题方法中获取承诺,您可以在 return 从内存中获取或询问后端之间切换,而无需更改商店之外的任何内容。

操作数

我的操作非常轻量级,它们对持久化它们封装的突变一无所知。它们只是带有从组件变异到商店的意图。对于较大的应用程序,它们可以包含一些逻辑,但绝不能包含服务器通信之类的东西。

组件

这些是您的 React 组件。他们通过调用商店的问题方法并呈现这些方法的 return 值来与商店交互。他们还订阅商店公开的 change 事件。我喜欢使用 高阶组件 ,这些组件只是包装另一个组件并将道具传递给它。一个例子是:

var TodoItemsComponent = React.createClass({
  getInitialState: function () {
    return {
      todoItems: null
    }
  },
  componentDidMount: function () {
    var self = this;
    TodoStore.getAll().then(function (todoItems) {
      self.setState({todoItems: todoItems});
    });

    TodoStore.onChange(function (todoItems) {
      self.setState({todoItems: todoItems});
    });
  },
  render: function () {
    if (this.state.todoItems) {
      return <TodoListComponent todoItems={this.state.todoItems} />;
    } else {
      return <Spinner />;
    }
  }
});

var TodoListComponent = React.createClass({
  createNewTodo: function () {
    TodoActions.createNew({
      text: 'A new todo!'
    });
  },
  render: function () {
    return (
      <ul>
        {this.props.todoItems.map(function (todo) {
          return <li>{todo.text}</li>;
        })}
      </ul>
      <button onClick={this.createNewTodo}>Create new todo</button>
    );
  }
});

在此示例中,TodoItemsComponent 是高阶组件,它包装了与商店通信的基本细节。它在获取待办事项时呈现 TodoListComponent ,并在此之前呈现微调器。由于它将待办事项作为 props 传递给 TodoListComponent,该组件只需要专注于渲染,一旦商店中发生任何变化,它就会重新渲染。并且渲染组件保持完全同步。另一个好处是 TodoItemsComponent 只专注于获取数据并传递它,这使得它对于任何需要待办事项的渲染组件都非常可重用。

高阶分量

术语高阶分量来自术语高阶函数。高阶函数是 return 其他函数的函数。因此,高阶组件是仅包装另一个组件并 return 其输出的组件。