避免具有异步数据依赖关系的事件链

Posted

技术标签:

【中文标题】避免具有异步数据依赖关系的事件链【英文标题】:Avoiding event chains with asynchronous data dependencies 【发布时间】:2014-11-09 17:21:22 【问题描述】:

Facebook Flux 调度程序explicitly prohibits ActionCreators from dispatching other ActionCreators。这种限制可能是个好主意,因为它会阻止您的应用程序创建事件链。

但是,一旦您的 Store 包含来自相互依赖的异步 ActionCreators 的数据,这就会成为一个问题。如果CategoryProductsStore 依赖于CategoryStore,则似乎没有办法在不推迟后续操作的情况下避免事件链。

场景 1: 包含某个类别中的产品列表的商店需要知道它应该从哪个类别 ID 获取产品。

var CategoryProductActions = 
  get: function(categoryId) 
    Dispatcher.handleViewAction(
      type: ActionTypes.LOAD_CATEGORY_PRODUCTS,
      categoryId: categoryId
    )

    ProductAPIUtils
      .getByCategoryId(categoryId)
      .then(CategoryProductActions.getComplete)
  ,

  getComplete: function(products) 
    Dispatcher.handleServerAction(
      type: ActionTypes.LOAD_CATEGORY_PRODUCTS_COMPLETE,
      products: products
    )
  


CategoryStore.dispatchToken = Dispatcher.register(function(payload) 
  var action = payload.action

  switch (action.type) 
    case ActionTypes.LOAD_CATEGORIES_COMPLETE:
      var category = action.categories[0]

      // Attempt to asynchronously fetch products in the given category, this causes an invariant to be thrown.
      CategoryProductActions.get(category.id)

      ...

场景 2: 另一种情况是,由于 Store 更改及其componentWillMount/componentWillReceivePropsattempts to fetch data via an asynchronous ActionCreator,而挂载了子组件:

var Categories = React.createClass(
  componentWillMount() 
    CategoryStore.addChangeListener(this.onStoreChange)
  ,

  onStoreChange: function() 
    this.setState(
      category: CategoryStore.getCurrent()
    )
  ,

  render: function() 
    var category = this.state.category

    if (category) 
      var products = <CategoryProducts categoryId=category.id />
    

    return (
      <div>
        products
      </div>
    )
  
)

var CategoryProducts = React.createClass(
  componentWillMount: function() 
    if (!CategoryProductStore.contains(this.props.categoryId)) 
      // Attempt to asynchronously fetch products in the given category, this causes an invariant to be thrown.
      CategoryProductActions.get(this.props.categoryId)
    
  
)

有没有办法避免这种情况而不诉诸延迟?

【问题讨论】:

对于场景#1,我将这种逻辑放在动作创建者自己中,这样存储只响应数据的变化。在存在异步逻辑的情况下,动作创建者有时会将多个动作分派到商店。我遇到了场景 #2,要么切换到 DidMount(在异步数据加载的情况下),要么偶尔推迟到 setTimeout @BrandonTilley 我已经澄清了这两个示例,在这两种情况下,用于获取类别中产品的 ActionCreator 都会触发异步 API 操作。 @SimenBrekken 你的问题解决了吗?请问***.com/questions/32537568/…可以看这里吗? 【参考方案1】:

每当您检索应用程序的状态时,您都希望使用 getter 方法直接从 Store 中检索该状态。 Actions 是通知 Store 的对象。您可以将它们视为更改状态的请求。他们不应该返回任何数据。它们不是您应该用来检索应用程序状态的机制,而只是更改它。

所以在场景 1 中,getCurrent(category.id) 应该在商店中定义。

场景 2 中,您似乎遇到了 Store 数据初始化的问题。我通常通过(理想情况下)在渲染根组件之前将数据放入存储中来处理这个问题。我在引导模块中执行此操作。或者,如果这绝对需要异步,您可以创建所有内容以使用空白板,然后在 Store 响应 INITIAL_LOAD 操作后重新渲染。

【讨论】:

我已经阐明了这两个示例,在这两种情况下,ActionCreator 获取类别中的产品都会触发异步 API 操作。因此,在场景 #1 中,我首先从我的 API 中获取类别列表,然后通过 API 获取该类别中的产品。至于场景#2,我做同样的事情,但只是在组件已安装并需要数据时。我不知道组件何时会被挂载,因此无法将数据输入根组件。 场景 1:产生 LOAD_CATEGORIES_COMPLETE 类型动作的动作创建者在哪里?似乎对 API 的调用应该转移到该动作创建者。我不清楚 LOAD_CATEGORY_PRODUCTS 是否有用。 场景 2:您的视图正在管理商店的数据。让商店管理自己的数据。 我认为在哪里可以调用 API 以获取更多数据存在一些混淆。在商店里这样做可以的。重要的是简单地确保当数据返回时,您然后通过操作进入流程,而不是直接修改存储。这确保了 store 可以控制自己,并且任何其他 store 都可以通过 Flux 方式获知相同的新数据。 场景 #1:我目前正在返回一个承诺,其中 CategoryProductActions.getComplete 是 LOAD_CATEGORIES_COMPLETE 操作创建者。场景#2:所以你的意思是我应该在给定类别中询问产品,而商店本身调用动作创建者?【参考方案2】:

对于场景 1:

我会从视图本身调度新的动作,因此将触发一个新的动作 -> 调度程序 -> 存储 -> 视图 循环。

我可以想象您的视图需要检索类别列表,并且默认情况下它还必须显示第一个类别的产品列表。

因此该视图将首先对 CategoryStore 的更改做出反应。加载分类列表后,触发新的 Action 获取第一个分类的产品。

现在,这是棘手的部分。如果你在视图的更改监听器中这样做,你会得到一个不变的异常,所以这里你必须等待第一个动作的负载被完全处理。

解决此问题的一种方法是在视图的更改侦听器上使用超时。类似于此处解释的内容: https://groups.google.com/forum/#!topic/reactjs/1xR9esXX1X4 但不是从存储中调度操作,而是从视图中执行。

function getCategoryProducts(id) 
setTimeout(() => 
    if (!AppDispatcher.isDispatching()) 
        CategoryProductActions.get(id);
     else 
        getCategoryProducts(id);
    
, 3);

我知道,这很可怕,但至少您不会有存储链接操作或域逻辑泄漏给操作创建者。使用这种方法,操作是从实际需要它们的视图中“请求”的。

另一个我没有尝试过的选项是,一旦包含类别列表的组件被填充,就监听 DOM 事件。在那一刻,你调度了一个新的动作,这将触发一个新的“通量”链。我其实觉得这个更整洁,但正如我所说,我还没有尝试过。

【讨论】:

以上是关于避免具有异步数据依赖关系的事件链的主要内容,如果未能解决你的问题,请参考以下文章

事件溯源和处理数据依赖关系

如何在没有 X11 的服务器上运行 R,并避免损坏的依赖关系

如何在 Flux 中处理具有依赖关系的数据组合和检索?

如何测试依赖于具有关系的 Eloquent 模型的类?

是否有任何标准的包含顺序以避免隐藏的依赖关系? [复制]

Spark DAG 依赖关系 Stage