2

我目前正在使用通常与 ReactJS 相关的通量模式开发原型应用程序。

在 Facebook通量/聊天示例中,有两个商店,ThreadStoreUnreadThreadStore。后者提供了一个同步读取前者内容的getAll方法。

我们遇到了一个问题,即派生存储中的操作太昂贵而无法同步执行,并且理想情况下会委托给异步进程(Web Worker、服务器跳闸),我们想知道如何解决这个问题。

我的同事建议从吸气剂返回一个承诺,即

# MyView

componentDidMount: function () {
    defaultState = { results: [] };
    this.setState(defaultState);
    DerivedStore.doExpensiveThing()
        .then(this.setState);
}

我对此并不完全满意。感觉就像是打破了模式,因为视图是变化的主要接受者,而不是商店。这是我们一直在探索的另一种途径 - 视图安装事件会发送一个更新派生数据的需求(如果需要)。

 # DerivedStore
 # =========================================================
 state: {
     derivedResults: []
     status: empty <fresh|pending|empty>
 },
 handleViewAction: function (payload) {
    if (payload.type === "refreshDerivedData") {
        this.state.status = "pending"; # assume an async action has started
    }
    if (payload.type === "derivedDataIsRefreshed") {
        this.state.status = "fresh"; # the async action has completed
    }
    this.state.derivedResults = payload.results || []
    this.notify();
 }

 # MyAction
 # =========================================================
 MyAction = function (dispatcher) {
    dispatcher.register(function (payload) {
        switch (payload) {
            case "refreshDerivedData": 
               doExpensiveCalculation()
                   .then(function(res) {
                        dispatcher.dispatch({
                            type: "derivedDataIsRefreshed",
                            results: res
                        })
                    })
               );
        }
    });
 };

 # MyView
 # =========================================================
 MyView = React.createClass({
     componentDidMount: function () {
         if (DerivedStore.getState().status === "empty") {
             Dispatcher.dispatch("refreshDerivedData");
         }
     },
     getVisibility: function () {
         return DerivedStore.getState().status === "pending" ? "is-visible" : ""
     },
     render: function () {
         var state = DerivedStore.getState()
             , cx = React.addons.classSet
             , classes = cx({
                "spinner-is-visible": state.status === "pending"
             });

         return <div {classes}>
                   <Spinner /> # only visible if "spinner-is-visible
                   <Results results={state.derivedResults}/> # only visible if not...
                </div>;
     }

 });


 # MyService
 # =========================================================

 # ensure derived data is invalidated by updates in it's source?
 OriginalStore.addListener(function () {
     setTimeout(function () {
        dispatcher.dispatch({
            type: "refreshDerivedData"
        })
     }, 0); 

 });

我喜欢这种方法的地方在于视图将 DerivedStore 视为它的视图模型,并且此类视图主要对其视图模型的新鲜度感兴趣。然而,我担心的是商店可能不同步。

那么我的问题:

  • 承诺方法可以接受吗?
  • 第二种方法更好/更差?如果是这样,为什么?
  • 是否存在解决此问题的“规范”方法?

PS:抱歉,如果此代码中有任何基本的 linting 错误,我在过去 3 个月一直在 Coffeescript 工作,它破坏了我的 linting 能力......

4

4 回答 4

2

所有异步操作都应该由操作的创建引起。异步操作的完成应该由另一个操作的创建发出信号。商店可能会监听这些动作,并发出更改事件。

在您的组件中,您侦听 DerivedStore 以进行更改。可以从任何地方创建操作,例如在您的组件或其他商店中。数据(最终)被派生,存储被更新,更改事件被发出,并且您的组件将事件有效负载应用于状态。

总而言之,您的组件实际上并不知道幕后发生的事情是同步还是异步。这很棒,因为它允许您在幕后进行这些性能更改,而不会有损坏组件的风险。

纯商店通常只有一个公共函数来获取商店的状态。在您的组件中,您应该只在 getInitialState 中调用它,或者更好的是:有一个 mixin 来执行此操作并为您添加更改侦听器。

于 2014-12-04T18:52:48.697 回答
1

如果我要以最可能的 Flux 方式创建异步进程,我会像 XHR 请求一样处理它——在 Action Creator 或 Store 中启动异步进程(以最适合应用程序的方式)然后在异步过程完成时调用一个新的 Action Creator 来调度一个新的动作。这样,多个 store 可以响应完成的昂贵的异步过程,并且数据流仍然来自一个 Action。

于 2014-12-04T18:35:53.657 回答
1

听起来以下 github 上的讨论的组合可以帮助您。

store.getItem() 可能需要异步服务器调用: https ://github.com/facebook/flux/issues/60

管理客户端数据量: https ://github.com/facebook/flux/issues/62

本质上,获取商店数据是同步的,然后组件可以告诉商店执行长时间运行的任务,但随后忘记了它。

一旦在 store 中完成任务,就会创建一个动作,并且会发生流程,此时组件可以从 store 同步获取所需的信息。

那有意义吗?

于 2014-12-04T13:52:18.407 回答
0

您也可以向您的商店添加一个处理程序,当您的商店中发出某个事件时调用该处理程序

所以让我们说在你的商店里你有一个方法:

Store = { ...

   addUnreadDoneListener:function(callback){
      this.on(SOME_EVENT_CONSTANT, callback);
   },

...}

在您的 componentWillMount 中,您可以使用组件的功能注册到此“addUnreadDoneListener”,然后每次您的商店发出此特定事件时都会调用该功能。

我个人在我的项目中也这样做。而且我认为这种方式很容易管理

希望这有帮助。

编辑:我忘了提及......我使用 Eventemitter 来做到这一点。

于 2014-12-04T13:10:51.703 回答