Tworzę prostą aplikację CRUD z wykorzystaniem kontrolera strumieniowego Facebooka do obsługi tworzenia i edytowania postów dla angielskiej strony edukacyjnej. Ja obecnie mam do czynienia z API, które wygląda tak:Jak obsługiwać zagnieżdżone wywołania api w strumieniu
/posts/:post_id
/posts/:post_id/sentences
/sentences/:sentence_id/words
/sentences/:sentence_id/grammars
na wystawie i edytowanie stron dla aplikacji, chciałbym być w stanie pokazać wszystkich informacji na danym stanowisku, a także wszystkich to zdania i słowa zdań i szczegóły gramatyki na jednej stronie.
Kwestia, którą wybijam, polega na ustaleniu, jak zainicjować wszystkie asynchroniczne wywołania wymagane do zebrania wszystkich tych danych, a następnie skompilowanie potrzebnych danych ze wszystkich sklepów do jednego obiektu, który można ustawić jako stan w mój komponent najwyższego poziomu. Prąd (straszne) przykładem tego, co starałem się zrobić to w ten sposób:
Górny poziom 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;
});
}
}
i tu jest moje PostsActions.js - pozostałych jednostek (zdań, gramatyki, słów) mają również podobne ActionCreators które działają w podobny sposób:
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)
});
}
}
głównym problemem jest to, że dyspozytor Flux rzuca „nie może wywoływać w środku wysyłki” niezmiennego błędu przy SentencesActions.fetch
nazywa w _handlePostsStoreChange
zwrotnego becau se, że metoda SentencesActions wyzwala wywołanie przed zakończeniem wywołania zwrotnego wysyłki dla poprzedniej czynności.
Jestem świadomy, że mogę to naprawić, używając czegoś takiego jak _.defer
lub setTimeout
- jednak to naprawdę wydaje się, że po prostu łatam ten problem tutaj. Zastanawiałem się również nad zrobieniem całej tej logiki pobierania w samych akcjach, ale to też nie wydawało się poprawne i utrudniłoby obsługę błędów. Mam każdą z moich jednostek rozdzieloną na własne sklepy i działania - czy nie powinno być jakiegoś sposobu na poziomie komponentu, aby komponować to, czego potrzebuję, z poszczególnych sklepów danego podmiotu?
Otwarte na porady od każdego, kto dokonał czegoś podobnego!
próbowałeś używać 'waitFor'? https://facebook.github.io/flux/docs/dispatcher.html – knowbody
@knowbody Tak, spróbowałem użyć 'waitFor', ale tak naprawdę nie rozwiązało to problemu, ponieważ problem polega na tym, że druga akcja zostanie wysłana, zanim pierwsza będzie mogła zakończyć. Być może jednak moje zrozumienie 'waitFor' jest złe i po prostu nie używam go poprawnie? – joeellis
@joeellis: czy możesz przygotować demo jsFiddle, demonstrując swoją sytuację problemową? –