Im using Flux, React and I have the components Simple and Messages:
jqXHR.done()
. Simple have a change listener to wait the dispatch of the result. If the result is null, I would like to show an error using my Messages component, so I call my MessagesAction.addError('My result is null')
.The problem occur when I receive the null result and immediately calls the MessagesAction.addError
inside the Simple component. In fact, I know that this can result in "Dispatch in the middle of a dispatch" error, but I don't know how refactor this code to show the error message using Flux.
Disclaimer 1: I can't use setTimeout
function to resolve this problem. This is not the right solution.
Disclaimer 2: The Simple
component represents any other component from app that will be show a message using the Messages component too.
Simple code:
findUser: function (value) {
UserAction.find(value);
},
componentDidMount: function () {
UserStore.addChangeListener(this.updateUser);
},
updateUser: function(){
var user = UserStore.getUser();
if (user == null){
MessagesAction.addError('My result is null!'); //here occur the error!
} else {
//set my user with setState
}
},
Messages code:
componentDidMount: function () {
MessagesStore.addChangeListener(this.addMessage);
},
addMessage: function () {
this.setState({
messages: MensagensStore.getMessages()
});
},
Thanks!
Well, the problem is that (at least in Facebook's Dispatcher implementation) you must not trigger any action inside the store callbacks, which would lead to undesired/unpredictable behaviour, like infinite dispatching, or inconsistent state changes (e.g. race conditions). This is due to the nature of a single broadcasting dispatcher.
The IMHO cleanest solution (without smelling waitFor()
) is to introduce an internal state in the triggering component. Using the state you trigger your message action in the next update cycle. This way, you don't have the problem of non-finished dispatch cylces.
// your component's implementation
getInitialState : function(){
return { user : undefined };
}
componentWillMount: function () {
UserStore.addChangeListener(this.updateUser);
},
componentWillUnmount: function () {
UserStore.removeChangeListener(this.updateUser);
},
componentDidUpdate : function(){
if(this.state.user == null){
MessagesAction.addError('My result is null!'); // no error anymore!
}
},
updateUser: function(){
this.setState({ user: UserStore.getUser(); });
},
Your top level container should listen for changes on both the UserStore and the MessageStore.
The MessageStore should 'waitFor' the Userstore, derive it's state (i.e update it's messages property) from the UserStore and then emit a change.
Something like this: Top level UserContainer component
findUser(value) {
UserAction.find(value);
}
componentDidMount () {
UserStore.addChangeListener(this.updateState);
MessageStore.addChangeListener(this.updateState);
}
updateState(){
this.setState({
user: UserStore.getUser(),
messages: MessageStore.getMessages()
});
}
renderMessages() {
if(!this.state.messages) return null;
return (
<Messages messages={messages} />
);
}
renderUser() {
if(!this.state.user) return null;
return (
<User {...this.state.user} />
);
}
render() {
return(
<div>
{this.renderMessages()}
{this.renderUser()}
</div>
);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With