如何处理嵌套的api调用 [英] How to handle nested api calls in flux

查看:135
本文介绍了如何处理嵌套的api调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Facebook的Flux Dispatcher创建一个简单的CRUD应用程序来处理英语学习网站的帖子的创建和编辑。我目前正在处理一个看起来像这样的api:

I'm creating a simple CRUD app using Facebook's Flux Dispatcher to handle the creation and editing of posts for an English learning site. I currently am dealing with an api that looks like this:

/posts/:post_id
/posts/:post_id/sentences
/sentences/:sentence_id/words
/sentences/:sentence_id/grammars

在应用程序的显示和编辑页面上,我希望能够在一个页面上显示给定帖子的所有信息以及所有句子和句子的单词和语法详细信息。

On the show and edit pages for the app, I'd like to be able to show all the information for a given post as well as all of it's sentences and the sentences' words and grammar details all on a single page.

我遇到的问题是弄清楚如何启动收集所有这些数据所需的所有异步调用,然后将我需要的数据从所有商店组合成我可以将一个对象设置为顶级组件中的状态。我一直在尝试做的一个当前(可怕)的例子是:

The issue I'm hitting is figuring out how to initiate all the async calls required to gather all this data, and then composing the data I need from all the stores into a single object that I can set as the state in my top level component. A current (terrible) example of what I've been trying to do is this:

顶级PostsShowView:

The top level PostsShowView:

class PostsShow extends React.Component {
  componentWillMount() {
    // this id is populated by react-router when the app hits the /posts/:id route
    PostsActions.get({id: this.props.params.id});

    PostsStore.addChangeListener(this._handlePostsStoreChange);
    SentencesStore.addChangeListener(this._handleSentencesStoreChange);
    GrammarsStore.addChangeListener(this._handleGrammarsStoreChange);
    WordsStore.addChangeListener(this._handleWordsStoreChange);
  }

  componentWillUnmount() {
    PostsStore.removeChangeListener(this._handlePostsStoreChange);
    SentencesStore.removeChangeListener(this._handleSentencesStoreChange);
    GrammarsStore.removeChangeListener(this._handleGrammarsStoreChange);
    WordsStore.removeChangeListener(this._handleWordsStoreChange);
  }

  _handlePostsStoreChange() {
    let posts = PostsStore.getState().posts;
    let post = posts[this.props.params.id];

    this.setState({post: post});

    SentencesActions.fetch({postId: post.id});
  }

  _handleSentencesStoreChange() {
    let sentences = SentencesStore.getState().sentences;

    this.setState(function(state, sentences) {
      state.post.sentences = sentences;
    });

    sentences.forEach((sentence) => {
      GrammarsActions.fetch({sentenceId: sentence.id})
      WordsActions.fetch({sentenceId: sentence.id})
    })
  }

  _handleGrammarsStoreChange() {
    let grammars = GrammarsStore.getState().grammars;

    this.setState(function(state, grammars) {
      state.post.grammars = grammars;
    });
  }

  _handleWordsStoreChange() {
    let words = WordsStore.getState().words;

    this.setState(function(state, words) {
      state.post.words = words;
    });
  }
}

这是我的PostsActions.js - 其他实体(句子,语法,单词)也有类似的ActionCreator,它们以类似的方式工作:

And here is my PostsActions.js - the other entities (sentences, grammars, words) also have similar ActionCreators that work in a similar way:

let api = require('api');

class PostsActions {
  get(params = {}) {
    this._dispatcher.dispatch({
      actionType: AdminAppConstants.FETCHING_POST
    });

    api.posts.fetch(params, (err, res) => {
      let payload, post;

      if (err) {
        payload = {
          actionType: AdminAppConstants.FETCH_POST_FAILURE
        }
      }
      else {
        post = res.body;

        payload = {
          actionType: AdminAppConstants.FETCH_POST_SUCCESS,
          post: post
        }
      }

      this._dispatcher.dispatch(payload)
    });
  }
}

主要问题是Flux调度员抛出因为SentencesActions,在 _handlePostsStoreChange 回调中调用 SentencesActions.fetch 时,无法在调度中发送不变错误方法在前一个操作的调度回调完成之前触发调度。

The main issue is that the Flux dispatcher throws a "Cannot dispatch in the middle of a dispatch" invariant error when SentencesActions.fetch is called in the _handlePostsStoreChange callback because that SentencesActions method triggers a dispatch before the dispatch callback for the previous action is finished.

我知道我可以通过使用类似 _.defer setTimeout - 然而,这真的感觉我只是在这里修补问题。此外,我考虑在动作本身中执行所有这些获取逻辑,但这似乎也不正确,并且会使错误处理更加困难。我将每个实体分离到他们自己的商店和行动中 - 在组件级别中是否应该有某种方式来构建我需要从每个实体的各个商店获得的东西?

I'm aware that I can fix this by using something like _.defer or setTimeout - however that really feels like I'm just patching the issue here. Also, I considered doing all this fetching logic in the actions itself, but that seemed not correct either, and would make error handling more difficult. I have each of my entities separated out into their own stores and actions - shouldn't there be some way in the component level to compose what I need from each entity's respective stores?

向任何已经完成类似事情的人提供任何建议!

Open to any advice from anyone who has accomplished something similar!

推荐答案


但不,那里在调度过程中创建一个动作是不可能的,这是设计的。行动不应该是引起变化的事情。它们应该像报纸一样通知应用程序外部世界的变化,然后应用程序响应该新闻。商店本身会引起变化。行动只是通知他们。

But no, there is no hack to create an action in the middle of a dispatch, and this is by design. Actions are not supposed to be things that cause a change. They are supposed to be like a newspaper that informs the application of a change in the outside world, and then the application responds to that news. The stores cause changes in themselves. Actions just inform them.

此外


组件不应该决定何时获取数据。这是视图层中的应用程序逻辑。

Components should not be deciding when to fetch data. This is application logic in the view layer.

Bill Fisher,Flux的创建者https://stackoverflow.com/a/26581808/4258088

Bill Fisher, creator of Flux https://stackoverflow.com/a/26581808/4258088

您的组件正在决定何时获取数据。这是不好的做法。
你基本上应该做的是让你的组件通过动作说明它需要什么数据。

Your component is deciding when to fetch data. That is bad practice. What you basically should be doing is having your component stating via actions what data it does need.

商店应负责累积/获取所有需要的数据。但需要注意的是,在商店通过API调用请求数据之后,响应应该触发一个操作,而不是直接处理/保存响应的存储。

The store should be responsible for accumulating/fetching all the needed data. It is important to note though, that after the store requested the data via an API call, the response should trigger an action, opposed to the store handling/saving the response directly.

您的商店看起来像这样:

Your stores could look like something like this:

class Posts {
  constructor() {
    this.posts = [];

    this.bindListeners({
      handlePostNeeded: PostsAction.POST_NEEDED,
      handleNewPost: PostsAction.NEW_POST
    });
  }

  handlePostNeeded(id) {
    if(postNotThereYet){
      api.posts.fetch(id, (err, res) => {
        //Code
        if(success){
          PostsAction.newPost(payLoad);
        }
      }
    }
  }

  handleNewPost(post) {
    //code that saves post
    SentencesActions.needSentencesFor(post.id);
  }
}

您需要做的就是收听商店。还取决于您是否使用框架以及需要发出更改事件的框架(手动)。

All you need to do then is listening to the stores. Also depending if you use a framework and which one you need to emit the change event (manually).

这篇关于如何处理嵌套的api调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆