javascript - 如何处理不断变化的嵌套 api 调用

标签 javascript reactjs flux

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

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

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

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

顶级 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 - 其他实体(句子、语法、单词)也有以类似方式工作的类似 ActionCreators:

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)
    });
  }
}

主要问题是当在 _handlePostsStoreChange 回调中调用 SentencesActions.fetch 时,Flux 调度程序抛出“无法在调度中间调度”不变错误,因为SentencesActions 方法在前一个 Action 的分派(dispatch)回调完成之前触发分派(dispatch)。

我知道我可以通过使用诸如 _.defersetTimeout 之类的东西来解决这个问题——但是我真的感觉就像我只是在修补这里的问题。此外,我考虑过在操作本身中执行所有这些获取逻辑,但这似乎也不正确,并且会使错误处理更加困难。我将我的每个实体分成它们自己的商店和操作 - 在组件级别是否应该有某种方式来组合我从每个实体各自的商店中需要的东西?

乐于听取任何已经完成类似事情的人的任何建议!

最佳答案

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

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

商店应该负责积累/获取所有需要的数据。但需要注意的是,在商店通过 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);
  }
}

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

关于javascript - 如何处理不断变化的嵌套 api 调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32240309/

相关文章:

javascript - Ionic Framework 干扰 contenteditable div + WYSIWYG 编辑器

javascript - 通过ajax存储数据然后访问该URL

javascript - Fabric.js - 如何为 Canvas 加载事件提供回调

javascript - 如何在不使用react-router的情况下知道页面刷新之前正在渲染哪个组件?

javascript - 如何为我的 Draft JS Editor 边框提供焦点效果?

javascript - 如何在javascript中显示/隐藏一个div?

javascript - 在 native 应用程序中使用中继

javascript - 无法使用 alt 操作触发 alt 存储 (Flux)

java - react 器: Flux<object> .subscribe() 与 .toStream()

reactjs - 如何使用 Redux 和 ReactJS 等声明式/函数式样式库来处理焦点?