【发布时间】:2023-04-03 20:22:01
【问题描述】:
我正在使用 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 方法在调度回调之前触发了调度上一个动作结束。
我知道我可以通过使用 _.defer 或 setTimeout 之类的东西来解决这个问题——但这真的感觉就像我只是在这里修补问题。此外,我考虑在操作本身中执行所有这些获取逻辑,但这似乎也不正确,并且会使错误处理更加困难。我将我的每个实体都分离到它们自己的商店和操作中 - 在组件级别中不应该有某种方式来组合我需要从每个实体各自的商店中获得的东西吗?
欢迎任何完成过类似事情的人提出任何建议!
【问题讨论】:
-
你试过使用
waitFor吗? facebook.github.io/flux/docs/dispatcher.html -
@knowbody 是的,我尝试使用
waitFor,但它似乎并没有真正解决问题,因为问题是第二个动作在第一个动作完成之前被调度.但是,也许我对waitFor的理解是错误的,只是我没有正确使用它? -
@joeellis:您可以整理一个 jsFiddle 演示,请展示您的问题情况吗?
-
不看所有代码很难说,但第一次调用 PostActions.get() 是触发全局更改,即触发 _handlePostsStoreChange,然后调用 SentencesActions.fetch()初始调度完成。我会推荐更细粒度的事件,即注册一个“ON_POST_FETCH”事件,在您发出FetchChange()时触发您的加载gif开/关,并注册一个特定的“POST_DATA_CHANGED”事件以响应emitPostDataChange()并调用您的SentencesActions.fetch() .不确定这是否会有所帮助,但我已经通过这种方式解决了类似的问题。
标签: javascript reactjs flux