13

Facebook Flux 调度程序明确禁止 ActionCreators 调度其他 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/componentWillReceiveProps 尝试通过异步 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)
    }
  }
})

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

4

2 回答 2

3

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

所以在场景 1中,getCurrent(category.id)应该在 Store 上定义一些东西。

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

于 2014-09-16T17:37:36.610 回答
0

对于场景 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 事件。在那一刻,你调度了一个新的动作,这将触发一个新的“通量”链。我实际上认为这个更整洁,但正如所说,我还没有尝试过。

于 2015-06-02T23:09:06.343 回答