我正在使用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-其他实体(句子,语法,单词)也具有类似的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) }); } }
主要问题是SentencesActions.fetch在_handlePostsStoreChange回调函数中调用时,Flux调度程序会引发“无法在调度中间进行调度”不变错误,因为SentencesActions方法在上一个操作的调度回调完成之前触发了调度。
SentencesActions.fetch
_handlePostsStoreChange
我知道我可以使用_.defer或setTimeout- 来解决此问题,但是确实感觉像是在这里修补问题。另外,我考虑了在动作本身中执行所有这些获取逻辑,但这似乎也不正确,并且会使错误处理更加困难。我将我的每个实体分离到各自的存储区和操作中- 组件级是否不应该有某种方法来构成每个实体各自存储区中所需的内容?
_.defer
setTimeout
接受任何已完成类似任务的人的任何建议!
但是,没有,在派遣过程中没有创建动作的技巧,这是设计使然。行动不应该是导致改变的事物。他们应该像报纸一样,将外部环境的变化通知应用程序,然后应用程序对该消息做出响应。商店本身会引起变化。行动只是告知他们。
也
组件不应决定何时获取数据。这是视图层中的应用程序逻辑。
您的组件正在决定何时获取数据。那是不好的做法。您基本上应该做的是让组件通过操作说明它确实需要什么数据。
该存储区应负责累积/获取所有需要的数据。不过,请务必注意,在商店通过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); } }
然后,您需要做的就是听听商店。还取决于您是否使用框架以及需要(手动)发出更改事件。