Pitfalls of Flux Dispatcher

Photo by Flemming Fuchs on Unsplash
<App>
<Posts />
</App>
  1. AUTH_SUCCESS is dispatched. Flux Dispatcher starts calling stores’ callbacks and does this in order.
  2. AppStore’s callback is called first, the store recalculates its state.
  3. All AppStore subscribers start to update. We have only one subscriber in our case — App component.
  4. The state was updated, and the App starts to rerender.
  5. This time isAuth is true and we start rendering Posts (this happens synchronously).
  6. componentDidMount() also happens synchronously. So, just right after the initial Posts render we start loading actual posts (Posts shows a Loader).
  7. Loading posts means dispatching LOAD_POSTS_STARTED first.
  8. What means we’re back in the Flux Dispatcher, which will throw the nasty error.
dispatch(action) {
this.$state = this.$reducer(this.$state, action);
this.$emit();
}
dispatch(payload){
// No more "Cannot dispatch..."
this._startDispatching(payload);
// Same try/finally as before.
// After state calculation notify all subscribers.
this._notifyAll();
}
_notifyAll() {
// In case of a nested dispatch just ignore.
// The topmost call will handle all notifications.
if (!this._isNotifying) {
this._isNotifying = true;
while (this._notifyQueue.length > 0) {
const notify = this._notifyQueue.shift();
notify();
}
this._isNotifying = false;
}
}

_invokeCallback(id) {
this._isPending[id] = true;
// Save callback from the store to the queue.
const notify = this._callbacks[id](this._pendingPayload);
if (notify) {
this._notifyQueue.push(notify);
}
this._isHandled[id] = true;
}
class PostsStore extends EventEmitter {
constructor(dispatcher) {
this.$emit = this.$emit.bind(this);
this.$posts = {};
this.dispatchToken = dispatcher.register(payload => {
switch (payload.actionType) {
case "LOAD_POSTS_SUCCESS":
// Don't forget to "return" here
return this.$loadPosts(payload);
}
};
}
$loadPosts(payload) {
this.$posts[payload.userId] = payload.posts;
return this.$emit; // A generic case with no args;
}
$clearPosts(userId) {
delete this.$posts[userId];
// When only a part of subscribers should update.
return () => this.$emit(userId);
}
}

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Anton Alexandrenok

Anton Alexandrenok

A /dev/null developer and entrepreneur.