Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Microblog 1
  • Overview
  • Transcript

4.3 Writing the Store Creator

You remember from when we did our overview of flux, that one of the most important components of a flux application is our store. And a store of course is where we hold the data that we're currently using in the front end of our application. Now a lot of the flux libraries include their own stores. However, since we're using Facebook's own implementation of flux, we don't have a store as part of our library. So we're going to build our stores from scratch. So in this lesson, we're going to build our store constructor. However, we're not gonna use the typical JavaScript constructor function we'll do something a little bit different. The first order of business is to install an npm package. So here in our terminal, I'm going to npm install object-assign and we're going to install version three. Object-assign is a function that will allow us to extend an object with the properties from other objects. And we'll use this in our store creator function. The other thing I want to do here is create a stores directory in our source folder. And this is where we're going to store all of the stores that we create. So back in our editor here, I'm going to create a new file in the stores directory, which I'll just call store.js. And I'll start by pulling in the assign method that we downloaded, so that will be require object-assign. The other thing we're going to need for our store module, is EventEmitter. Now, if you've used node at all on the back end, you're probably familiar with the EventEmitter constructor, which allows you to create an object which has the emit and on functions, which allow you to listen for events and then with the emit function you can trigger those events. It's a publish and subscribe system, and we're going to need that in our store. So we're gonna create a variable here called EventEmitterProto. And this is going to be, we'll do require event, and we'll get the Event.Eventemitter and then we'll say .prototype. So this will be the prototype of that function, which we are going to use in the extension of our own stores. So when someone requires this module, they're going to get the store module. And we want that store module to have an extend method, which they can use to create their own stores. So to create this extend method, let's do exports.extend. And then we can have a function here. And when someone requires this module, they'll have an object with this extend function which they can use to create their own stores. So we're going to create stores by combining a number of objects. To start with, let's create the object that will be our final store. So I'll just call this store, and let's give this two initial properties. We'll have our data, which will be an empty array, and since this is kind of a private property, let's just call underscore data, and then we'll also have a set of actions that this store is listening for. And that will be an empty object. And so this is our initial store object. But now, we want to extend this store object with a number of other prototype objects. Basically copying those prototype properties onto this object. And we can do this with this assign function. So we'll say assign and we can say store as the first parameter. And the first parameter of assign will be the object that receives the properties and then any subsequent objects will be the objects whose properties are copied onto the initial object. So, for example, we could put the EventEmitterProto as a first parameter here, and now all of the methods on the EventEmitterProto object will be copied to our store object, which will turn our store object into an EventEmitter object. All right, so we know we want the EventEmitter prototypes, so that's good. But we also has some other methods we want. Above our exports function here, let's create an object that we're going to call storeMethods. And in this object we're going to put a bunch of methods that every store will need to have, so we can also add that one to the list here. So we can say storeMethods, and now this store object will be given the EventEmitter properties and the store method properties. Now, before we fill in those storeMethods, I'll add one more object to this list, because each individual store that we create will need to have its own custom methods and properties, and those will be passed into this function. So I'll take a methods object here as a property to this function. And then we can accept that as the last parameter to our assigned call here. Excellent, so that is how that will work. Okay, so now that we have that set up, let's go and fill in our storeMethods. The first thing we want is an initializer. Now, we will expect that the methods object that is passed into the extend function will have its own initialize function. Because right here, after we assign all these methods to the store, we're going to call store.init, which is just what we're gonna call our initialize function. However, if the methods object doesn't have that, then we need to make sure that our storeMethods have it so that we won't get an undefined function error when we do this store.init call right here. So let's just make this an empty function right there as our first parameter. Now the next method that we're gonna have is the set method. And this method will take an array of objects and it will add those objects to the data array that this store has. And of course, these objects are going to be the models or records that our application is using. Now we don't want this data array to have duplicates and so we need to have some way of making sure that the objects in this array are not already in our data array here. And we're going to assume that every object here is going to have an ID property or more specifically a client ID or a CID property which we know is what our database, local adb gives to each one of our records. And so, let's do this. Let's first get a list of all the ID's that are currently in this data's array. So we can do is say var, and we'll say current id's, and we'll say this._data, and then we'll just map this. And for every one of the models in that array we'll just return m.cid. So now we have an array of those IDs. Then, let's take the array that was passed into this function and we're gonna filter it and we will return only the items whose IDs are not in the current IDs array. So we can do currids.indexOf(item.cid) so we'll get the ID off the item and check to see what it's index is in the current IDs array. And we will only return true when that is equal to minus 1. All right, so now we'll only have the items that are not currently in our data array, and then we can just do forEach and let's do this.add.bind(this). Now, we don't currently have this add function, so that's gonna be our next one here. Add is kind of like set except it takes only a single item, and so we'll say item here, and we'll make this very simple. We'll just do this._data.push and we'll push the item into the array. There we go. And those are the two methods we'll need to add items to our store. Now we also want methods to get all of the records, or to get an individual record by its ID. So let's have an all function here, and this is very simple. This can just return this._Data. Trickier thing to do is to get an individual record by its ID, so we'll have a get function, and this will take an ID as its parameter, and then we can just return and let's do this._data.filter. And we will only return the one that where the item.cid matches the id that was passed in. Now, of course we know there will only be one item with the given ID, however filter will still return an array, so let's just get the zero width item or the first item in that array. Excellent. So, now we can just get all of them or just get one if that's what we want. Now we know that our stores will need to be listening for actions that occur, either in the user interface or coming from the server so that they can update their data appropriately. However, stores will also send actions and this of course is why we've used the EventEmitter prototype in our object here. Our store needs to be able to say, I have changed in some way. And so, we need to allow other objects to subscribe to our store's change events. So, let's create a constant up at the top here for the event name. Kind of like we did in our constance file. And I'll just call this, CHANGE_EVENT. And this can just be a string. And we'll just say CHANGE. And then, let's create a couple of functions here. First of all, we need a function for registering an event handler. So we'll say addChangeListener, and this of course will take a function as its' property. And in here, we can just say this.on and our store objects have an on method, remember, because we have extended it with the EventEmitter prototype object. So we can say on the CHANGE_EVENT, when that happens call this function. Now, of course, the reversal of this is the removeChangeListener function, and we'll just pass this through to this.removeListener, and once again we're listing for the change event and we want to remove the same function that was passed in. Now when we're using our store, and when we change that store we need a way to emit that change event. So let's create an emitChange function here. And this we'll just do this.emit and we'll emit that CHANGE_EVENT, there we go. So these three functions will make it very easy for us to update everybody else about changes inside of our store. We're also gonna create a bind function, and here's how the bind function works. Remember, as we said, our stores need to be aware of the actions that are taking place in our application. For example, we know that perhaps when we have a list of chirps coming from the server, when that response happens and ends successfully, there will be a action going through our system saying that we got the chirps. Well, our store needs to be aware that we have got those chirps, and it needs to be listening for that so it can say, okay, I want those chirps, I want to load them inside of my data array. So we need a way to bind an action to our store. And so that's what this bind function is going to be. This bind function will take an actionType and an action function that it will run when that actionType occurs. And right now all we're gonna do is add it to that actions object that we had in our initial store object down here. So we'll say actions and then we'll say [actionType] = actionFn. Now you might notice right away, we have a little bit of a problem here. What this would mean is that if, for example, this store here was listening for the got chirps action and it wanted to run a function there, it could only ever run one function for the got chirps action. If we tried to bind a second function to it, it would overwrite that first function in this actions object. So we should probably change the way this works, just in case we ever want to bind multiple functions. Let's say if this.actions actionType already exists, then, what we can do is say this.actions[actionType].push, and we'll push in the action function. So that will be an array. However, if this is the first time that we are creating something for this action, then we'll say this.actions[actionType] will equal an array with only one value in it right now, actionFn. Okay, and lets remove that first line. There we go. All right. Now binding to actions through this bind function doesn't actually set up any kind of a connection with our dispatcher, which is the object that will actually be dispatching the events. And so what we're gonna do is use our dispatcher down here in exports.extend. And the idea here, is that this bind function will be used inside of the init method. So when we run store.init() here, that will populate this actions object, with any of the actions and their handlers that we wanna use. So underneath store.init(), we can register each of those actions with the dispatcher. So let's require our dispatcher, that is in one directory level above the store's directory. So we can say ../dispatcher and then we can say dispatcher.register, to register a function. Now, you might recall we did this in the dispatcher file itself. If I open up the dispatcher file, you can see we did dispatcher.register and we registered our function here so that every time an action occurs, we're just gonna log that to the console. Also remember that these action objects that we saw are the ones that we have in our actions object. So if I open up source/actions.js, you can see right here where we are doing our dispatching, these objects are going to have an action type property and a data property. So, let's come back to store.js now after remembering that and we can see that our register function takes its call back and this function here will take an action as its parameter. And so in here we need to see, is there anything in the action's object that wants to handle this action? So we can say if (store.actions[action.actionType]). So if that value exists, well then we know we have an array of actions that needs to be called. So let's loop over that. I'm just gonna copy this line right here, and I'll paste in here. And so we can loop over this array, let's do forEach. And for each function in this array, we need to call it passing it the data from this action. So we'll say fn.call, we'll just pass null as the value for this, that's not really important in this case. But then as a property to it, we will put action.data. And this is the data property of the action object coming in. And now, with this run, our store is ready to handle any actions that come through. So the very last thing we'll do in our exports.extend function here would be to return our store. Excellent. So now we have our store constructor here. Now, as I mentioned, a lot of the other flux libraries come with some kind of store functionality built in. So kind of what we created right here would be already built in to one of those libraries. And when you use it, you would not have to write all this code, you could just extend their own store. However, this gives you a little bit of an idea of what might be going on behind the scenes. Then of course, every library store functionality is gonna be a little bit different, but these are some of the basics that they should all have something to similar to. So in the next lesson, we'll look at creating a specific store objects. The chirps store.

Back to the top