4.2 Creating Constants and Actions
The first order of business for our client-side code is creating a set of actions. We'll do that in this lesson.
1.Getting Started2 lessons, 03:51
2.Project Setup3 lessons, 20:58
3.Server-Side Code6 lessons, 53:00
4.The Client Side16 lessons, 2:43:53
5.Conclusion1 lesson, 00:29
4.2 Creating Constants and Actions
Now that we have a basic understanding of how flux works, let's begin to create our application. And where we're going to start in this lesson is creating our dispatcher, and then creating the actions that we will dispatch through the dispatcher. So the first thing we're going to do is install Facebook's implementation of Flux. Now as I mentioned in the previous lesson, there are several implementations of Flux, and each of them have a different set of features. But for this course, we're going to use just the basic implementation of Flux that Facebook itself provides. Now even though this is a front end package, Facebook kind of expects you to use something like Browserify, which is what we're doing in this project. So it makes it available as an npm package. So I'll go ahead and install Flux by doing npm install flux. And I want to install Flux version 2, and I will not forget to save that to our package.json file. Once we have Flux installed, we can come over to our editor and let's create a dispatcher.js file, and I'm gonna put it in this source directory here. So let me go and open a source directory, and I'll say dispatcher.js and in this file, we'll start by pulling flux in. So remember, even though we are writing code that will be run in the browser, we can use require statements as if this is node, because we're going to be compiling this through Browserify before we actually view anything in the browser. So we'll pull flux in like that and then let's create a dispatcher and this dispatcher will just be new flux.Dispatcher. And it's really that easy. The only other step now is to export the dispatcher from this file. So I'm gonna do this kind of like we've done in a previous lesson. I'll do module.exports and I'll just put this in here so we're assigning this new dispatcher object to both the dispatcher variable and to the module.exports property so that when we require our dispatcher module somewhere else, this dispatcher object will be available. Now one thing I like to do when I'm developing a Flux application is right within my dispatcher file actually render a function with the dispatcher that will make it a little bit easier to track our actions. Here's what I do. I do dispatcher.register. Which is the method that we use to register a function. So, we have our function, it will of course take an action and remember, the way Flux works is that all the actions that pass through our dispatcher will be sent to all registered functions. So, this function here will be run every time our dispatcher receives an action. So then, all I do is just console.log the action. And this way, as I'm building my application, I'll be able to see in the terminal, what actions are occurring, and when. And that just makes it a little bit easier to understand the flow of data through our application. So this is all we need to do for our dispatcher. Let's now create the action functions that we can use to dispatch events through our dispatcher. Now, before we can create those actions we need to have names for those actions. Now we could just use string names for each one of those actions. However, one of the conventions with Flux is to use a set of constants to hold your actions. This has a number of benefits. One of them is that you can see a list of all your actions in one place. But a more important benefit is that it makes it a little bit easier to debug your code if you are accidentally mistyping your action names. Let me show you what I mean. And to do this I'm gonna create a constants file. So we'll call this constants.js and it goes in the source directory. And in here we don't actually have to require anything. We're just going to do module.exports and we're going to export an object here. So the pattern that's usually used is, we have a constant name, so for example, I might call it CHIRP and the convention is to make that all uppercase. And then we'll just use the very same thing as the value for this property. Now after we have all of our constants here, in another file we'll refer to them by, well, of course, first we'll require this constants file, but then we can do something like constants.CHIRP. And this is what we'll be typing every time we want to use the CHIRP action, instead of actually typing the string CHIRP. The benefit to this is that if we mistype CHIRP when it's a string, we won't know about it. So for example, if I type CHIRP like this and transpose the R and the I which is something I did pretty often when I was developing this application. Then if we have some piece of code waiting for this action it's never going to occur. However, there is not going to be any error message telling me I misspelled the action because this is just a string, right, and it doesn't know this string refers to an action. However, if I misspelled CHIRP in this context then we will get an error saying that the constants object does not have a property of this misspelled name. So, that is the benefit to using constants, however you don't actually have to use constants if you don't want to. This is just a convention that a lot of people use when using the flux pattern. Let me also point out that when you're using the constants convention, it's not important that the property name be the same as the property value. What's important is that the property values be unique, because eventually like I said we'll be referring to these actions as something like constants.CHIRP. And so if multiple constants have the same value, well then we may have two actions occurring at once, or other problems like that. So the important thing to remember is that the attribute values are unique. This would actually be a great place to use an echma script six symbol. So we would just go like this. Create a new symbol or we could even give that symbol a name. So we could say symbol is CHIRP, which may make it a little bit easier if you're logging these to the console. However, echma script symbols like this are not currently supported in Internet Explorer or Safari at the time of this recording so we're just going to stick to regular string constants. All right, so now that you kind of understand the constants invention, let's make a list of all of the constants That we want. Now remember, these constants are of course our list of actions. Each one of these constants is going to represent one of the actions that is going to occur in our system. Now the way we're building this application, we kind of have two different types of actions if you will. One is an action that will initially occur by something that the user does in the user interface. And actually there's only one of those. And we're looking at it right now, CHIRP. So the user will type some text into a textbox, hit the CHIRP button. And when that happens, the CHIRP action will occur. The other type of action is when some data comes back from the server. And we need to respond to that in some way. So the next action is CHIRPED. This CHIRPED action is going to occur when a CHIRP is saved. So if you think about kind of the round trip process here, the user will type something into a box, they'll click a button, the CHIRP action here will occur which will trigger the actual CHIRP that was just written to be sent back to the server to be saved. When the response comes back from the server saying that the CHIRP has been saved, this CHIRPED event will occur. And so the convention I'm using in our actions here is a present tense action name is something that occurs in the front end. However, a past tense word is used to refer to something that happened on the server and now we are responding to that on the client side. So we have CHIRP and CHIRPED. Another action that will occur is we will have CHIRPS coming to us from the server, right? So as other users who we follow are CHIRPING, we want to view those CHIRPS. So we'll be getting those from the server. And when those CHIRPS come in, the GOT_CHIRPS action will occur. So let's create a GOT_CHIRPS constant. We're also going to need some actions that are related to users. So let's have the GOT_CURRENT_USER action and that will be for getting information about the currently logged in user. And then we're also going to need GOT_USERS. Which we will use when we get a list of users from the server. Of course we'll need those whenever we display a list of users to the currently logged in user. The final set of actions that we'll need to perform in our application is following and unfollowing other users. Now of course following and unfollowing begin in the client UI. So we will have present tense verbs for this. So we can say FOLLOW and UNFOLLOW. But then those actions will be sent to the server, and we want to know when they have successfully completed. So we will have a FOLLOWED action and our very last action, the UNFOLLOWED action. Excellent. So that is the list of all of the actions that we will need for this project. So now that we have these actions as constants, let's go ahead and write some functions which will dispatch these actions to the dispatcher. So I'm going to do this in an actions file, so in the source directory, again, let's create a file called actions.js. And we're going to need those constants, and we'll also need our dispatcher. So, let's say var dispatcher equals require and it's important, when requiring on the front end, just as we do on the back end that when we're not using packages, we get the relative paths correct. So, I have to say in this directory, get the dispatcher file. And we also want to get the constants file. Now if you look at most Flux examples you'll probably see them creating actions something like this, we could do exports.chirp to create a function for our CHIRP action and so we have a function that will take some data and then from within this function we'll just say dispatcher.dispatch and then we dispatch some object with some properties to our dispatcher. Now there are two properties that every action object that we dispatch to the dispatcher will have. The first one is the action type and this will be the constant that matches our action function's name. So in this case that's going to be constants.CHIRP. And then we also need the data that goes along with this action. So, in this case, it's whatever data we pass as a parameter to this function. Now we could go through and create a function like this for every single one of our constants. However, I've looked at that and I said, well, there must be a little bit of an easier way to do this because all of these functions look so similar. So instead why don't we just take our list of constants here and automatically generate these functions. So here's what I'm gonna do. Let me just comment this out and I'll leave it at the bottom here. And let's use that list of constants to convert to our action functions automatically. So what I can do is say object.keys, and we can use this to get all the keys from constants. And then let's do a four each here and loop over these keys, and in here we will create these functions as we loop over our constants. So the first thing we have to do is create a name for our action function. So I'm just going to call this funcName and what we want to do is this each one of our constants is completely upper case and its spaced with underscores and we want to covert these to camel cased names with leading lower case letters so what we can do is we can start by saying the function name equals the key and then we'll say dot split. And we'll split it on the underscore and then let's map over each one of those items. And in our map function we're going to take each one of the words by itself and then we're also going to take the second perimeter to map which is I, our index in our array here. So then within our map function, we'll say if i equals zero, meaning this is the first item in the array. Well then the first word in our camel case function name should be all lowercase. So we'll say return word.toLowerCase. Otherwise if it's not the first word, we want to keep the first letter in capital, and then lowercase all the rest of the letters. So we'll say return word zero plus word.slice1. So we'll slice the first letter off. And then to lower case for the rest of it. So, if we have the first word, it'll be all lower case. Otherwise we'll have the first letter upper case, and the rest of it lower case. So, we could see if this is working. Let's, after this, just do console.log function name. And now, currently this action file won't actually be used, because it's not referenced from the main js. So, let's move over to source/main.js and right now we have these other things that we were doing earlier, so let's just remove that and let's just say var actions equals and will require the actions file. Now we don't need to do any more than that because just requiring actions will actually run the actions file. But what we do need to do is recompile this so that this file will be run. So let's come back to the terminal here and I'll run gulp, which should run our Browserify task. As you can see that's happened so now I'll run node server to go ahead and start our server. And then if I head to local host port 3000, I'll need to log in first with the username and password we created previously. And now I should be able to look in the console here. And you can see that we have a bunch of arrays returned. And these arrays are what you would expect map to return. So you can see for single word actions like CHIRP and CHIRPED they're just completely converted to lower case. However for multi-word actions like GOT_CHIRPS or GOT_CURRENT_USER, you can see they have been split at the underscores and the capitalization is correct. So the first words are not capitalized, but subsequent words are. So now we just need to join these arrays back into strings. So back in our application here, we can end after our map call here, we can say join and now we're ready to create our function. So, let me copy the lines of the function that we created right at the beginning and I'll paste those in at the top here. And then we can say exports. And instead of saying dot function name, we can use square brackets and pass it the name of the function that we just created. And so then the function takes the data. And we have our dispatch call right here. The only other change we need to make is instead of using the CHIRPS constant for all of our actions, we change this, and instead, we use square bracket key, so we use the initial key that was passed in here, and this will get the value for the constant that we are currently looping over. And so now we should have functions with the names that we choose, which will dispatch the right action type for each one of our actions. So we can see this in action one more time if we go back to main here. Let's loop over actions here. So again, since this is an object, I'll use object.keys to loop over actions. And inside this function we can just do console.log the key. And then underneath that let's do console.log and we'll say actions[key] and that will be a function, so we'll just say function.toString. Now before we can see this, remember we need to recompile our front end code. Now, in this terminal tab, I have the server running. So I'm gonna open up a new terminal tab and move into our project. And in here, I can just run gulp watch. And what this will do is watch our project and automatically rerun our Browserify task whenever we make any changes to the file. Let me come back here, and I'm just gonna make a few changes to this file. And if I save it, now when I move back to the terminal, you can see that these were rerun. So, with the server running and the watch task watching our files, we can now just come back to the browser and refresh the page whenever we make some changes, so if I refresh the page, notice exactly what we have going on here. We have all of our functions being displayed now because of the way we wrote our functions we can't actually see what the constants square back at key value is however, you can see that each one of our actions has a function that looks very much like what we would have written if we were doing it the typical way. However, we've automated this and made it a little bit easier instead of writing the same function over and over we only had to write it once. And of course the other beautiful thing about this is that if we add or remove any constants later we don't have to worry about updating our actions file. Now, you might be wondering is this a good idea to automate the creation of our action functions in that way. Well, as I've said I haven't actually seen anyone else do this, so maybe there's something that I haven't thought of or haven't realized yet. However it's been working for me. And that's one of the neat things I think about Flux. It's a pattern, but that doesn't mean that there's some wiggle room for your own personal taste. So if you prefer to write your own action functions, you can go ahead and do that. If you wanna try what I've shown you here, that can work for you too. You're free to do whatever you want. So now that we have our dispatcher in place and a bunch of actions to work with, we're ready to create a store. So we'll look at that in the next lesson.