In this lesson we'll create our Flux application's store which will manage the state of our application.
[00:00] In a Flux application the store manages the state of our application, so here in stores I'm going to create a new file called app-store.js. For the sake of brevity I'm going to be pasting in some code in this lesson.
[00:15] We are importing dispatch and register from our app dispatcher, our app constants, and we're also important importing event emitter from Node. Going to create a constant variable here called changeEvent, it's going to be equal to change, and this is the value that we're going to broadcast to the rest of our application every time there's a change, we're going to have a variable here called catalog equal to an empty array.
[00:41] I'm then going to prefix that with an underscore to indicate that it's a private variable. We are going to populate that with dummy data. So here I've just got an array that's going to generate nine widgets, each with an id, a title, a summary, a description, and a cost. Going to have a variable here called cartItems representing the items that our user has in the cart. I'm going to be pasting in a handful of methods, so the first one is remove, it is simply going to splice the item as it uses find index which is an ES6 array method of our item from the cartItems.
[01:20] Here we have a method called findCartItems which simply finds a cartItem in our cart using the array method of find. IncreaseItem does exactly what it sounds like, it takes in the item and increases its quantity key. DecreaseItem decreases it by one, and if it is in fact zero at that point it will call our removeItem which we created up here.
[01:43] AddItem will simply use our findCart item which we created up here, and see if the item is in fact in the card, it will increase that item, otherwise it will push the new item using object.assign adding the quantity of one to the item. Finally we have cartTotals which is basically just going to tell us what's in the cart by iterating through the cartItems adding up the quantity of the items as well as the total of items which is quantity by cost and then returning an object with a quantity and a total key, each with a matching value.
[02:18] So we can go ahead and create our appStore variable which is what we'll export by default when we're through, and we're going to go aheand assign that to object.assign, now object.assign if you're not familiar with it allows us to extend one object with new properties. The object we're going to be extending is eventEmitter.prototype, we're going to be passing in a new object.
[02:42] Let me scroll this into view, now the first thing we're going to add is emitChange which will be a method that simply calls evenEmitter's emit and pass in our change event. Now that value we assigned right here in the very beginning to a string of change. The next thing we'll do is add changeListener, which will take in a callback and add a new on to our eventEmitter.prototype so we're saying this.on change event callback.
[03:19] Fairly simple, we're going to copy that and use it for our remove change listener, and rather than on, we're going to say remove listener on the change event remove this call back. Pretty straightforward, need a comma here, comma there. We'll have a method here called getCart simply going to return our cartItems which we set up previously.
[03:44] Have a method here called getCatalog, and this is going to return what we're going to end up doing is merging our catalog items with our cart items and that way we have a whole list of our catalog items, we also know which items are actually in the cart, so to do that we're going to return catalog.map take in our item, we're going to return using object.assign once again.
[04:13] We're going to have an empty object, and to that we're going to add item and then we're also going to add cartItems, and we're going to find in our cartItems, so we'll say this is the cartItem, we're just going to say that the cartItem.ID is equal to the item.ID.
[04:35] Then lastly we're going to have dispatcher index, now this doesn't really need to be a key in our appStore, but if we have multiple stores, multiple dispatchers, this becomes important later, but for right now we're going to go ahead and set it as a key, which is just equal to register, it's going to take in a function that takes in our action, or our payload, which is what we're sending to our dispatcher.
[04:59] Then we're just going to switch on the action.actionType and again, I'm just going to paste in some code here for the sake of brevity, clean that up a little bit. All we're saying is that if the case is addItem, then call our addItem method with the item, if it's remove, call removeItem with the item, if it's increase again increase with the Item, and decrease call decrease with the item. In each of these cases one of these methods will be called, and each one will be passed in our item.
[05:32] Then after our switch, we'll go ahead and appStore.emitChange and finally we will export by default our appStore. Then one other method that we're going to need to expose here is getCartTotals which will simply return cartTotals which we created up above.
Old habits, good catch. This could easily be updated to:
register( action => {
Joe... When you defined AppStore, the first argument to Object.assign (called the target object) should have been “{}”. As it stands, EventEmitter.prototype has now been mutated to include the subsequently defined properties (getCart, etc.).
Joe… When defining “dispatcherIndex”, you placed “AppStore.emitChange()” after the switch block (rather than within each case statement of the switch block). Consequently, AppStore will end up processing call-backs for every action coming through the dispatcher, even though most of these actions will not affect the state of AppStore.
All of the actions in dispatcherIndex have an effect on the state of AppStore.
Thanks for the feedback, Stephen. Extending EventEmitter.prototype presents no ill side effects in this application, but yes, I could have done it the way you've suggested.
All of the actions in dispatcherIndex have an effect on the state of AppStore.
I agree. But as your application grows, you will likely to add another store for a feature which is not core to the shopping cart (e.g. wish lists). This new store will have a new set of action types associated with it. As the Flux dispatcher will route all actions to all stores, the AppStore will receive actions which it will not process. When this happens, the present code will cause AppStore to signal a change (call its subscribers) even though its state has not changed.
Thanks for the feedback, Stephen. Extending EventEmitter.prototype presents no ill side effects in this application, but yes, I could have done it the way you've suggested.
As presently coded, the AppStore object IS EventEmitter.prototype (which has now been modified with extra methods). There has been no inheritance (I use the term loosely) of functionality, they are the same object! My concern is that when you create your next store (e.g. wish lists), you will again extend EventEmitter.prototype. Now both stores will be the same object, will have a single call-back list, and possibly overwritten methods. Not good.
What's the point of the third argument? return Object.assign( {}, item, _cartItems.find( cItem => cItem.id === item.id)) Do we really need it?
Never mind. Figured it out.
When this happens, the present code will cause AppStore to signal a change (call its subscribers) even though its state has not changed.
Yes ! I got the problem today and raged for a few hours before remembering your comment. So thank you, it helped a lot, but we should find a way to make it more visible
In
app-stores.js
, why did you usefunction
instead of an arrow function indispatcherIndex: register(functi...
?