如何处理嵌套的api调用

joe*_*lis 12 javascript flux reactjs

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

/posts/:post_id
/posts/:post_id/sentences
/sentences/:sentence_id/words
/sentences/:sentence_id/grammars
Run Code Online (Sandbox Code Playgroud)

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

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

顶级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;
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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)
    });
  }
}
Run Code Online (Sandbox Code Playgroud)

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

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

向任何已完成类似工作的人提供任何建议!

Moe*_*ler 5

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

也

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

Bill Fisher,Flux的创建者/sf/answers/1860726591/

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

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

你的商店看起来像这样:

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

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