What do you want to learn?
Skip to main content
Building Applications with React and Flux
by Cory House
Get started with React, React Router, and Flux by building a fast, data-driven single page application.
Resume CourseBookmarkAdd to Channel
Table of contents
Who Is This Course For?
Hi, I'm Cory House, and welcome to Building Applications with React and Flux. In this course, we're going to explore how to build Single-Page Applications using real-world tooling and configuration. We're going to use our variety of new technologies including Browserify, Gulp, and Node.js. And to keep things interesting, the application we're creating will be a web app for administering Pluralsight courses and authors. We'll decompose the application and the multiple nested components using React, and we'll handle client-side routing via React Router. Finally, we'll create unidirectional data flows using Flux. So here's the course outline. In this module, I'll introduce the Core Technologies that we'll be using to build a React application. In Module 2, we'll set up a rich development Environment that will help us quickly build and run our application, and we'll receive rapid feedback when we make typos and errors, and don't worry, if you're already familiar with the technologies covered in Module 2, I'll provide you a link to the finished Environment Setup so that you can speed ahead and jump straight to learning React. Once we have our Environment Setup, it's time to dive into React in Modules 3 through 8. We'll set the stage by discussing React's Core Concepts, like JSX and the Virtual DOM, in Module 3. Then in Module 4, we'll begin building our app by creating our first React Components. This work will continue in Module 5, as we learn about React's Lifecycle and put it to use for initializing and wiring up more features within our components. In Module 6, we'll consider how to create reusable components. We'll compose our components together and we'll pass down data from our parent components down to child components. In Module 7, we'll shift gears away from React Core, so we can implement rich, client-side routing using React Router, the most popular routing solution for React. We'll close out our coverage of React in Module 8 by implementing rich Forms that handle input changes, validate data, and notify the user of issues. The final two modules are the capstone of the course. In Module 9, we'll cover all the new terminology that's part of Flux, including Actions, Dispatchers and Stores. In the final module, we'll clean up our data handling by utilizing the Flux pattern for unidirectional data flows. When we're done, we'll have a clean scalable solution for rich, component-based web applications, and at the end, I'll wrap up with a short challenge for you to put your newfound skills to work by enhancing this new application that we've created. Now I know that's quite a list, but don't worry. This course assumes no previous knowledge of these technologies. So, who is this course for? Well, there's three core audiences. First, if you've never tried React and you want to learn it by building a real application from the ground up, then this course is for you. You'll learn it by example. I assume no previous knowledge of React. And if you're building a real app in React, you'll likely need it to find routes so that your users can navigate through your application, share deep links, and transition smoothly between different application states. React Router is a popular option for routing that's supported by the Facebook team, so we'll use it to handle our applications routing. Finally, maybe you enjoy React, but you haven't yet tried Flux, or maybe you've tried Flux, but you're still unclear about things like unidirectional data flows, Actions, Dispatchers, Stores, and all the various new jargon that comes along with Flux. If so, then this course is for you. I'll demystify all of that. We'll explore the core concepts behind Flux and then use it to handle events and data flows in our application. Next, let's talk about the interesting innovations in React that make it such an exciting technology for building complex web applications.
Innovations in React and Flux
For a Component Library, we're going to use React. React is a project from Facebook that handles a very specific set of concerns, packaging, composing, and rendering HTML components. Because of this focus, React is lightweight and makes it easy to create complex UIs by composing many simple components together. Because React is so focused on this one goal, it can even be used along with other frameworks like Angular and Backbone. React utilizes a virtual DOM. This makes it very fast. When UI changes, React compares the old state to the new state in memory, and then it updates the DOM in the least expensive way. This saves resources and makes React notably faster than many of today's popular alternatives, and with the power of Virtual DOM, React can even support rendering on both the client and the server. This, of course, is critical for building isomorphic apps and really sets React apart from other traditional libraries and frameworks that are currently very DOM-oriented, like Angular or Knockout.
React Router Overview
Since React is a small, focused view engine, it has no opinion on how to handle routing, and for small apps, you may have no need for a fully-featured router, but as your app grows, you'll likely find you want to split the application into multiple client-rendered pages with deep linking, and that's where a routing library comes in handy. So for a routing app, we'll use React Router. React Router offers a simple nested approach to routing. You declaratively configure routes and the ability to nest routes means that you can support complex UIs that are many layers deep. React Router is both used by Facebook and supported by Facebook, and if you're already familiar with Ember, you'll find React Router is very easy to pick up since it's heavily inspired by Ember's approach to routing. Regardless, I'm sure you'll find the declarative and centralized approach easy to understand.
Flux is a uni-directional data flow library created by Facebook to complement React. Now, Flux is really just a branding name for Facebook's implementation of a pattern. This is a uni-directional data flow pattern. So there are a variety of alternatives out there including DeLorean, Fluxxor, Fluxy and Reflux, but we're going to use Facebook's Flux implementation in this course because it's very popular. Flux avoids a more traditional MVC-style pattern and instead implements one-way data flows. This tends to make large applications easier to predict and reason about by avoiding complex interactions that can occur between multiple view and view models in traditional MVC.
I want to make a quick note before we dive in and install these final libraries. As we just saw, the technologies in this course are proven and used on some of the largest websites in the world, such as Facebook and Yahoo. However, at the time of this recording, React and React Router haven't yet reached a 1.0 release, so their APIs may make breaking changes from what you see in this course. I'll do my best to keep this course updated but in order to assure that you can follow along, I'm going to have you install these specific versions. These are the current versions as of the time of this recording. Now, I suggest that you install these exact versions to assure that you can run everything that I show. Now once you've finished the course, I recommend checking the changelog on GitHub for React and React Router. That way, you can see what things have been released since this course has been published.
What About Testing?
What About ES2015?
Versions Used in This Course
All right, let's wrap up this module by reviewing what we've covered so far. We reviewed the core technologies that we'll be using to create our example app, and we configured our environment so that we can put these technologies to use throughout the course. We'll be using npm as our Package Manager, React for Components, React Router for Routing, Flux for our Data flows, Browserify as our Bundler, and Gulp for Builds. So that's the tech stack. In the next module, we'll dive into React and see how its powerful Component model makes building high-performance applications a breeze.
Demo: NodeJS Install
To get started setting up our environment we're going to download Node.js. You can get Node.js at nodejs.org. Just click on the Install button, and this applies whether you're on Mac or Windows, and then once it's downloaded we'll just double click on it, fire it up. This should all look pretty familiar to you. We'll just click right through and let it install. And we'll just click Finish when completed. And now that we have Node installed, let's jump over to the command line and just make sure it all works. So you could choose the command line of your choice. If you're on Windows you could use obviously the Windows DOS command line. I like to use Git Bash when I'm on Windows. Then of course if you're on Mac then you have terminal just built right in. So I'm going to type node -v and we can see that I'm running node.12.6, so it looks like Node installed successfully. And if you'd like you could also just type node and then get a command line here where you could send commands to Node and just see them echo back. So I'm going to clear that out, and just move over to the directory where we're going to do our work. So to begin configuring the environment I'm going to type npm and nit. We're right now just in an empty directory. And when I hit npm and nit I'm going to get prompted, we're going to call this project psadmin, stands for plural site admin because what we're creating is an administration tool for plural site. And we'll just leave the version as is, description empty, and really just leave all these empty. It doesn't matter much. I'll go ahead and put my name in here, and is this ok? We'll say yes. Just hit enter again. So now we've created a package.json file. This is the file where Node keeps track of the packages that we've downloaded and other pieces of configuration. So I can cat this file and then see the contents. So as we can see there's not much in here right now but as we start to install packages we'll watch those packages get added to this list. So this is really just a manifest that keeps track of what packages we're using with Node.
Demo: Gulp Install
To get started we're going to install a couple packages using npm, which is Node's package manager. So we'll type npm install and we'll use the save flag here, which really just tells Node to write these package references into package.json so that we have a list of the packages that we've installed. And what we're going to install initially is gulp, gulp-connect, and gulp-open. And then we'll just hit enter. So we're installing three different packages at the same time. With npm we can just do multiple at the same time just space delimidating these lists. Okay, those three installed, and now if I cat package.json we can see that those three dependencies have been written to package.json. And now let's jump over into our editor so that we can configure the rest of the application. I'm going to use Sublime Text. Sublime Text is just one of many options that you can choose from. Alright, so we are going to create both a source and a destination folder. So I'm going to right click, create a source folder, and right click again, and create a dist folder. And the convention here is that we will put all of our source code in the source folder and then using our build process we will write files to the dist folders. So the dist folder is what we'd actually push out when we publish our application to the Web.
Demo: Gulp Configuration
So let's begin configuring our gulp build process. I'm going to right click and create a new file and this new file is going to be our gulp file so I'm going to save it right here in the root as gulpfile.js and this is an important convention for gulp. And don't worry if you're not familiar with gulp, I'm going to walk through it at a high level so you should get a good feel for what it's useful for and you'll just want to follow along exactly with what I'm doing. I like to begin any js file by adding "use strict" to the top. At the top of our gulp file we're going to reference any other libraries that we need to handle our build process. So I'm going to begin by requiring gulp, which we will obviously need, and then I'm going to require those other libraries that we've already installed because we know we'll need gulp-connect, we know we'll need gulp-open, and I'll add some comments in here so that we know what this is for. This is going to run a local dev server. So this is how we will fire up a Web server to be able to see our application. And this will open a URL in a Web browser. We use gulp-open to make that happen. Let's create our first task. This task is going to start a local development server and in gulp the way you create tasks, you say gulp.task and then you name that task. I'm going to call this one connect. And then I will create a function, the second parameter is a function. And in here we will call connect.server and then we just pass it a chunk of json that configures our server and we need to tell it the root of our server, which will be dist, and tell it the port, and I'm just going to reference a config that I haven't created yet but we'll create it in a moment, and we also need a base path so I will say config and we'll call this the devbase URL. And then finally I'm going to turn on live reloading. This is handy because I can simply tell this task that any time that our files change it should reload our source in the browser. So we've created our first task here. What I'm missing, though, is I don't have variables to find for these configurations. So I like to keep all of my configuration for my gulp files at the top, and I will just configure it like this. I'm just going to choose a random port, we'll say 9005. If that one for some reason is taken on your machine, just choose a different one. And our base URL will be localhost. And then I'm going to create a list of paths. We'll have a variety of paths that we're working with. The first one will be HTML. And what we do in gulp is define what are called globs. And a glob looks a bit like a regex. I can say that I want, for instance, any HTML files in the source directory to be matched. So that's what this is saying, go into the source directory and find anything that ends in .html. And I'm also going to define my dist folder which we've created and the path to that is ./dist. And this looks okay although I just realized I am missing some slashes here. So we have a task that will now connect to a local dev server but we also need a task that will open a given file in the server. So let's create another task and we'll call this one open and it's going to have a dependency on the task we just created up above, which is connect. So what we're saying here is when you run the task open, first run the task connect. So this will run after we've opened our development server. And in this task we're going to call gulp.source again and we need to pass it place to find our file and I'm going to point to a file that we haven't created yet but we will do that in just a second, and then I'm going to pipe that output to open and with open I'm going to pass it a URL and that URL is going to be using our devbase URL plus a colon, cause I also need to define a port. So I will say config, port, and then a slash right here. So that defines our open task. So what this task is saying is go get index.html and then open it in the browser at this URL which is going to use our devbase URL up here. So it's going to open it at localhost at this port. Now we haven't created the file that I just referenced yet, index.html, so let's go ahead and do that. And we'll create it under source and I'm just going to paste in some basic HTML, the title in here, so we at least have something to start with, and I will save this as index.html. This will be the starting point for our application. And notice that I saved this under the source directory because this is where we'll be saving all of our source. Now remember, though, that we need to get anything that we put in source over into dist, because we're going to have a build process that takes all of this, bundles it up, and places it in the dist folder. So we need another task in here that will handle our HTML files and get them moved over. So I'm going to create another task and call it html. And I will again call config.paths.html, which we defined above, and I will pipe that to gulp.dest. So this is where I pass it a destination. And I have defined up above the dist directory and that's where we want our file to land. And then one other thing that we want to happen is when this happens we'd like to reload anything that's running right now in our local dev server. So I'm going to call connect.reload, because connect is the dev server that we're using. So this is our completed task and what we're saying here is go get any HTML files, put them in the destination path, which we defined up here as .dist, and then finally, after you've done so, reload using connect. And connect is the dev server that we downloaded from npm. Alright, one final piece of configuration that I'd like to complete, we're going to want a default task just to make it easy for us to do all of our development throughout the course, so I'll name this task default, and then I just provide it an array of tasks that should run by default, and I'm going to put open and html in here. So now what this is saying is if I go to the command line and just type gulp it's going to run the HTML task and the open task. So let's jump over to the command line and see if this works. If I hit LS right now we can see there's our gulp file so we know we're in the right path as long as we can see the gulp file, and now if I just type gulp that's going to run gulp. And it doesn't see default in our gulp file, so let's go back over here, it looks like I just didn't save. Okay, it doesn't like something else. I think I know what it is. Looks like I am missing a comma right here. So we'll save this and rerun gulp. And now we see gulp fire up a tab. And of course we don't see anything out here in the window itself but we do see the title getting applied here and if I viewed source we can see the source that we have added in and then a little bit of extra down here, which is plugged in for live reload to happen. So now we have a handy way to be able to just type gulp on the command line and open up our index.html in the browser. That's great, but another step that we can do to make this even nicer is to go back into Sublime and create another task. And I'm going to create a task to watch files. So we will call this task watch. What we want to do here is watch files so that every time that we make a change gulp knows about it and it reloads the browser. So I'm going to call gulp.watch and then I will pass it config.paths.html, so I'm watching my html path and any time that something changes in there I want to run the html task. And now that we've created this watch task we want that to run as part of the list of default tasks. So each time that I type gulp on the command line it's going to run the html task, the open task, and the watch task. So let's go back to the command line here, and I'll kill the previous process. I'll type gulp, which should open up new browser. So now we have our tab running right here, and let's test this. We'll go back to our editor. And I'm going to open index.html, and I will say, "Hi Viewers!" and I'm going to hit Save, and when I do we instantly see that pop up over here and if I add a few question marks and hit Save we instantly see those changes as well. So this should help us do rapid development as we move through the course. And now that that's set up we can shift our focus to setting up Browserify which will allow us to use npm packages within the browser.
Demo: Browserify Configuration
Demo: Bootstrap Install
Demo: ESLint Configuration
Demo: React, React Router, and Flux Install
Alright, now it's time to install the three core libraries that we're exploring throughout this course, react, react-router, and flux. And I'm going to have you specify the exact version as you install these so that you can make sure that you follow along and are able to use the exact same APIs that I'm covering in this course, so be sure that you enter react 13.3, react-router 13.3, and flux 2.03, and that's the at sign that sits in between the name of the package and the version. So just make sure that you use this exact command to install all three. I'm going to fire that off. Good deal, looks like that's done.
React Core Concepts
React and MVC
You're likely familiar with the traditional MVC model. MVC stands for Model-View-Controller. ASP.NET MVC utilizes this pattern. Popular client-side frameworks like Angular called MV* or MVW, to stand for MVWhatever, since the traditional controller doesn't really exist. React doesn't dictate how you handle data flows, routing or other concerns in your applications. It focuses solely on being a simple, composable component library. It's this simplicity that makes React such a powerful and flexible solution to component-based application development. Now, some people say that React is the V in MVC, but in actuality, well-composed React Components will fill both the V and the C in MVC. As you'll see, with React, you create controller views which end up operating a lot like controllers that you're used to in MVC. A controller view is basically a fancy term for React views that handle data concerns and compose other dumb components together, so your controller view has a number of child components that sit under it. We'll check out an example of this in a future clip, but in short, controller views promote reuse and separation of concern.
Two-way Binding Risks
Now if you've worked in Knockout, Angular or Ember, the thing you're most likely to miss when you first work with React is Two-Way Binding. I've enjoyed Two-Way Binding. It's a simple way to eliminate boilerplate and quickly deliver applications, and for many applications, it's a powerful and efficient pattern, however, when designing React, Facebook chose to avoid Two-Way Binding. They found Two-Way Binding led to unpredictable interactions in their applications. One UI change would trigger a corresponding model change, and that model change would trigger another model in UI changes. These interactions can become hard to predict and follow in a complex application, particularly, when many developers are interacting in the same codebase. Frankly, I feel these concerns can be largely mitigated through careful application design, but I will concede that once you get used to unidirectional data flows in Flux, the source of any data change is easy to track down since all the changes flow in one direction. You'll see more on this when we get to Flux in a later module.
HTML in JS: A Justification
To understand the Virtual DOM, I think it helps to start with a story. Imagine I do professional photography. I have a complicated stage setup like you see here. I have an assistant who helps set up all of these lights. It's a lot of work to get them in the right spot. I just show him a picture like this so that he knows how I need the lights set up today. Now here's the thing, since the lighting setup is generally the same from day to day, the most efficient thing for him to do is to compare my desired setup to the current setup. I likely want nearly the exact same setup from day to day but maybe without this one light, or maybe with this light placed in this spot instead of this spot. Obviously, the most efficient thing for him to do is to compare the current setup to my desired setup. That way, he just has to change a few things in a minor way to get to the desired end state, but what if he doesn't even try doing this comparison? What if instead, he ends up tearing things down and rebuilding them blindly based on my picture? That's a lot of time-consuming work and this manual setup is slow. In the same way, the Virtual DOM saves time. Just like the stagehand manually setting up these lights, updating the DOM is slow. To avoid this issue, React's Virtual DOM compares the current state of the DOM to the new desired state and determines the most efficient way to update the DOM. See, in today's Two-Way Binding libraries, like Angular or Knockout, when a piece of data changes, the UI is redrawn. However, updating the DOM is an expensive operation and by expensive, I mean it requires a lot of processing because redrawing a large section of the DOM is inefficient. React recognized this problem, which is why they created an abstraction over the DOM that they call the Virtual DOM. Traditional Two-Way Binding approaches don't have a Virtual DOM, so when state changes, they immediately update the DOM to reflect the new state. In contrast, React monitors the values of each component's state. When a component's state changes, React compares the existing DOM state to what the new DOM should look like. It then determines the least expensive way to update the DOM. Now this sounds complicated, but it's all behind the scenes and done for you. This approach avoids layout thrashing, which is when a browser has to recalculate the position of everything when a DOM element changes. Being efficient is increasingly important in a world where more people are using mobile devices. Mobile devices vary widely in their CPU power and conserving battery life is a concern as well. The good news is, there's nothing extra that you have to do to enjoy the performance benefit of the Virtual DOM. When you update the state of a component, it happens automatically. The comparison happens in memory, so it's typically very fast. Traditional frameworks say, "Oh, looks like this array has changed. "I'll just iterate over the entire array "and redraw everything." React, instead, compares the old state to the new state to determine the cheapest way to make the change, so React simply removes the row from the DOM. This is much less expensive than redrawing the entire table. That said, Angular recently added the "track by" feature, which helps avoid this issue, and Ember's new Glimmer rendering engine uses a similar approach to React to enhance performance. And it's hard to argue with the results of the Virtual DOM's performance. What you're seeing here is an example from Khan Academy. Khan Academy moved from Backbone to React. Notice how much smoother the React version renders. This graphic shows how React and Backbone behave when a computationally expensive operation, which in this case is converting raw text to a fancy math equation, is performed. As you can see, the Backbone version is choppy, but the React version is smooth. This is because React is only updating the DOM with the text the user is typing, while Backbone is rendering everything from scratch on each keypress. This makes React much more efficient. You can improve performance even further by declaring a shouldComponentUpdate method on your components. In this method, you can tell React not to update the DOM at all even when certain data changes. This is useful when you know the DOM shouldn't change even though the data just changed. And you can also consider using immutable data structures to make React even faster. Immutable data structures can't be changed, instead, a fresh copy is made when you need to change data. This makes React even faster because it can do reference comparisons. And of course, the Virtual DOM isn't merely about performance. React also offers Synthetic Events. Synthetic Events abstract away browser-specific event quirks and allow React to optimize the performance of attaching event handlers behind the scenes. This means in React, that you can, for example, attach event handlers to each row in a table. React will intelligently attach the event handler at the highest level, for efficiency. The Virtual DOM also allows React to render on the server, where no DOM exists at all, and because the DOM is abstracted away, React can even be used for implementing components in native applications using React Native. For the second time, we won't explore Isomorphic rendering and React Native in this course. Those are both large topics that warrant their own courses. But the bottom line is, Virtual DOM doesn't just buy us performance, it buys us the flexibility to do these other innovative approaches.
React: Creating Components Introduction
Now we've set the foundation by covering a few core concepts in React so we're ready to start building components. In this module we'll begin building our example application which is a web-based tool for managing Pluralsight authors. Let's begin by diving right into the code. In this first coding module we'll create our first React components, implement simple, custom routing and create a centralized layout for our application. And we'll close out this module by quickly discussing the naming conventions that I'll use throughout this course. All right, open up your editor of choice, mine happens to be Sublime Text. Let's start coding.
Demo: First React Component
Demo: Simple Routing
Demo: Centralized Header
Now that we've created a couple of separate components it would be nice if we had links at the top of each page that allowed us to navigate between these different pages. Let's create a new folder and I'm going to call it common. This is where we'll put any common components that are used throughout our application. Within here I'll create a new file and this file I'm going to call header.js. I'm just going to paste in the markup for this because it looks a lot like the other React components we've created so far. Again, the only difference is we have some different markups sitting in here. And there's of course some different classes, all of these classes are just useful so that Bootstrap will style our navigation for us. You can see I'm using the same anchors that we were putting in manually in the previous clip where we were navigating between home and about. The other thing you do see here is the Pluralsight logo so I'm going to place that within images. We will create an images folder under source. And I'm just going to paste it in to our images folder that I just created. There's the logo file and of course if you download the source for this module you'll find the image file there, or you could always just go out to Pluralsight's website and save an image that you can use as the logo for your application as you're building right along with me here. Now that we've created our centralized header we just need to reference it in main.js. Right now our whole application just displays a child component but we'd like to have the header as well. Let's reference the header. We will require under components/common/header. And then I simply add a tag right here so that the header is rendered as part of our markup. Now that we've wired this up we should be able to go back to the browser and see that it is already reloaded. Here's our navigation across the top and I can click on about and click on home and see that that works. Now the one problem is why do we see a broken image here? Well, remember when we set up gulp we never created a task to handle our images so let's jump over to gulp and create a task that will move our images from the source directory over to the disk directory. We'll open up the gulp file and I'm going to define a path right here that we'll use for images. That will be a lot like the others, ./source/images and for now we'll just create a simple rule that says any images within this folder or what we want to work with. If you watched the previous module you already saw me create quite a few other gulp tasks so I'm just going to paste this one in, this shouldn't be too surprising to you. What we can see it does is it uses the path I stated up above and then it migrates everything over to the disk folder under images and then reloads. I'm also going to publish the favicon which is placed within the root. I'm going to copy the favicon and place it out here in the root of source, which is where favicon traditionally sit, and those will also get copied over to the disk folder. If you're not familiar with favicons those just get requested by the browser by default so that you have a nice little icon in the URL and within bookmarks. And if you want a favicon to use to follow along with me you could always just pull one off of Pluralsight's website or of course download the code for this module. That's why we'll quickly open the containing folder here, go under source and paste my favicon right here, sitting out in the root like we'd expect. Now that we've defined the images task we of course want that to run by default. Now that we've finished updating our gulp file we'll save our changes and go back to the command line and we will stop our gulp process and rerun it since we've made those changes. This assures that our latest gulp file has now run. Now when the application loads we can see our logo sitting up here at the top. We know that our logo was properly migrated over to the disk folder. Now our application's starting to take shape. We can navigate between different pages, we have image handling, we have linting, we have a build process. The next step is how do we interact with data coming from the server. In the next clip let's interact with a mock API so that we can picture how we make AJAX calls to get and receive data from the server.
Naming React Components
All right, we're off to a great start. We dove into the editor and we've created our first React components. We implemented simple custom routing so that users can navigate our single page application or replace this simple routing setup with a more robust approach in a later module when we start using React Router. We created a centralized layout that composes a header module and we closed out the module with a short discussion on React naming conventions that we'll use throughout this course. Now that we have a running application that can be navigated, we're ready to expand the functionality of our app as we utilize props, state and the component life cycle to create dynamic components. That's our focus for the next module.
In the previous module, we created our first React components and we utilized custom routing and a centralized header to support client-side navigation. Now it's time to discuss the tools we need to create more dynamic components. So we'll begin this module by discussing props and state. And we'll learn how to handle dynamic child components by assigning keys. Then we'll discuss React's multiple lifecycle functions which give us hooks for initializing our components and attaching behaviors at specific points in time. Once you understand these concepts, you'll be ready for the second half of this module where we get back into the code and we put these concepts to use. We'll create a simple mock API that we'll use throughout the course, and then we'll create some dynamic components that put these principles to work.
Props and State
Data for a given React component is held in two places, props and state. Props is short for properties. You can think of properties a lot like HTML attributes. Props allow you to pass data down to child components. For example, if I had a component that displayed a username, a parent component could pass down a username prop. Props are immutable. Since they're passed down by the parent, they're effectively owned by the parent. State, in contrast, is mutable. And since state is mutable, you should strive to only utilize state on your controller views. In other words, only use state on the top level component. Pass data down to your child components via props. You can optionally define a getInitialState function. In this function, you can set the initial state for your component. This should typically only be done in your top level component, also known as your controller view. You can also define default values for properties on your component using the getDefaultProps method. Inside this method, you can return a set of properties that your component should use by default if the parent component doesn't declare a value.
Now here's a surprising reference. If you've ever worked with asp.net webforms then this aspect of React components will feel very familiar to you. Each React component has a lifecycle. So there's a list of functions that allow you to hook into various parts of the component's lifecycle. Let's quickly explore when and where each of these lifecycle functions are called, and consider why each is useful. We'll also consider where each of these functions run. Since React was built from the ground up to be rendered on both the client and the server, also known as isomorphically or universally, some of these functions run on both the client and the server, while some of them run only on the client. componentWillMount runs immediately before initial rendering. So this function's a great spot to set your component's initial state. componentDidMount runs immediately after render. By the time this function's called, the component's DOM exists. So this is a handy spot for integrating with other frameworks such as third party component libraries. This is also a good spot set times and make AJAX requests since you now know the component is rendered in the DOM. componentWillReceiveProps runs when the component is receiving new properties. In other words, when properties have changed. This is a place to set state before the next render since this runs just before the new properties are received. shouldComponentUpdate runs immediately before render and when new props or state are being received by your component. The big reason this function is useful is for performance. Why? Well, sometimes props and/or state changes, but the components doesn't actually need to re-render because the data change doesn't affect the DOM. When this is the case, you can return false from this function to avoid an unnecessary render call. componentWillUpdate runs immediately before rendering when new props or state are being received. This function is a useful place to prepare for an update. But take note that you can't call set state in this function. componentDidUpdate is evoked immediately after the component's updates are flushed to the DOM. So this function allows you to operate on the DOM immediately after the component has been updated and re-rendered in the DOM. The final lifecycle function is componentWillUnmount. This function runs just before component is unmounted from the DOM. So this is a great place to clean up by destroying any related resources or DOM elements that were created when the component was mounted.
Keys for Dynamic Children
Also, keep in mind, when you're creating multiple child components dynamically, you need to provide a key for each child component. As children are added and removed, React uses this key to assure that child components are properly reordered or destroyed. So in this example, I'm using the author's ID field as the key. This is often the primary key for the corresponding database record, but it need not be. You just need to declare a unique ID for each specific record.
Demo: Mock API Setup
We just learned about the React lifecycle, props, state, and keys, but before we put all this to use, let's create a simple mock API to give us some data. Of course, any application that's very interesting is likely going to write some data to the server and get data from the server. So what we haven't handled yet, is making any server calls. What I'm going to do is mock out a traditional web API. We're not going to create a real one because that's really not necessary for us to learn how to interact with an API. I'm going to create an API folder and what you can imagine is this is just an abstraction, a centralized point where we make all of our AJAX calls within our application. And then within here, I'm going to create a new file and I'm going to call this file authorApi because the point of this application is to administer authors for Pluralsight. And then I'm really just going to paste in the rest of this file. And we'll walk through it really quickly because none of this is really specific to React flux, React router, this is really just a way for us to mock out an API to pretend that we're interacting with the server. So I've defined this API and you can see some of the calls we're going to work with throughout the rest of the course, where we're going to need to be able to getAllAuthors, getAuthorsById. We'll need to saveAuthors. And again, when we save an author we're really just pretend that we called the server. We're not actually going to do so. So that way you can easily work on this without having to set up any real API. And then when we delete an author we don't actually call the server since one doesn't exist. We just log to the console. Simple enough, but our UI will still get updated. Now, one thing I am referencing is some author data. So I'm going to create another new file under here and call that authorData, and then I'm just going to paste in a few authors for Pluralsight so that we'll have this list of authors just to populate our initial list of authors in the application. And since we're not actually hitting a server when we make changes to the app. Just be forewarned that each time you reload this application, you'll be seeing this hardcoded data that we've created. You'll be able to interact with the app and change the data, but of course that data will only be persistent within your browsers since we aren't hitting any database to save the changes. And one other library that I'm using here just to make interacting with data sets easier is lodash, so we'll need to go over to our command line and install lodash using NPM. So I'm going to say npm install save lodash. Great, so looks like lodash installed just fine. And now that we have this mock API we can interact with it by creating a new component that displays this data.
Demo: Dynamic Data and Lifecycle
So let's go under components and create a new folder called authors. And within authors, we'll create a new file and we'll call this file authorPage. The point of this page is to display a list of our authors. So after adding use strict, going to need a reference to React as usual, and we'll also need a reference to that authorApi which we just created which sits couple levels up under API under authorApi, and now we're ready to create our new React component which we'll call authors. And we'll call React.createClass as usual. And then within here, we'll have our render function and what we want to do is render a table. So this is the first time that we've had to deal with iterating over a list of items. For starters, render will want to return some mark up. Let's create a div that will wrap everything we're returning. Again in React, you need to always have a top level component. You can only have a single top level component. So that's why we often use a div to wrap all of our mark up. And of course, we're going to need an h1 that shows Authors. Since we've created this new page, we also need a way to navigate to it, which means we have to go back to main and add a reference to this new page. This is our authors page. We'll say components slash Authors slash authorPage. And then we need a case that will handle this as well. And the child will of course be Authors. And again, this boiler plate isn't pretty, but in the next module we're going to see how React router cleans all this up and really gives us a nice scalable foundation. But I wanted to start with something simple here before we pulled in another dependency. Finally, we need to go find our Navigation file and add a link in here to the new page that we've just created. And of course, the link for this will be Authors. Before we try to run this, I realized I made just a couple of mistakes in the Author page. We made a semi-colon after return and of course, we can't forget module.exports to export our Author page out. And now if you have both running, you should be able to move over the browser and see that all three of our links are showing now and we can navigate between... So we can see our new Authors page is loading. So we're ready to add some more content to our Author page. So let's create a table that will display the list of authors. We'll give it a className of table so the bootstrap will style this. And then we need to define a header for this table. We'll display the ID for the author and also display their name. Then after the head, we will define the body of the table. And this is where we can call another function because what we're wanting to do is iterate over a list of items. And in this case, we're going to want the state for this particular component. We will see say this.state.authors.map. Now we haven't defined this on state yet. So we'll go up and do that in just a second. And we also haven't defined this yet. We're going to call it createAuthorRow and pass it a reference to this so that it knows the context where it's being executed. All right, so this seems a little strange right now. Let's wire this up so that it makes sense. What we need to define some initial state for our component. So we're going to use the getInitialState lifecycle method. We'll define it right up here. And we know that the initial state that we need is Authors. So I'm going to return an object. I'm just going to set Authors to an empty array. That will be our initial state in case there aren't any authors. But, we know that we want to get data from the API. And we'll do that within a different lifecycle component which is componentWillMount. And within componentWillMount we will set the state. And when you set state, you don't just manipulate the state. You need to use the setter. So we'll say setState and pass it the new state we'd like to use. So this will be setting authors... And to set authors, we're going to use the authorApi and the call getAllAuthors. Now since this is a mock API, this is a synchronous call. since that mock API just has hardcoded data in it. Obviously, if we were making an AJAZ call for instance that was asynchronous, then you'd need to deal with that asynchronous nature. You can choose to do so using call backs, using promises. Whichever style that you like. But here, just to keep it simple we're just making a synchronous call to get this data from our mock API. Now the other thing we haven't define yet is this createAuthorRow function. Because as we're iterating through this list of authors, we were going to call createAuthorRow. So let's find that right here within our render method. So we'll say createAuthorRow is a function that takes an author. So this will be a function that we can iterate over. Within this function, we're going to return our table row. Each table row is going to need a key because when we create multiple instances of a given element using React components, React needs a key so that it can keep track of these different elements. And for instance, as we remove elements and we add elements, this allows React to maintain proper state for this to also make sure that the ordering of these elements doesn't get destroyed as we re-render the application. So there's our key. A common key that you would use would be a primary key from a database, so we are using the ID for the authors since that is a unique key we can rely upon. And now, we will create a table data and an achor within it, because what we want to do is allow viewing a specific author from this page. And so the link for that we'll say is going to be Authors and then the authorId. In fact, I'm going to use curly braces here so I can define a variable or reference a variable in this case. So what this is saying, the hyper reference is going to be slash slash authors slash, and then the authorId. And then within the text that we've displayed to the screen, we'll use the authorId. We'll close the anchor tag, close the table data, and then our other field here is going to show the author's name. So we'll have the author's first name, space, and then the author's last name. We'll show both of these fields together, side by side, and the nice thing about JSX is this space will get rendered. And one other typo I made. I need a comma here since we are providing a comma delimited list of functions as we define our class. And I just realized also, I should have a tr here, not a td. And we will close this return statement with a semi-colon. And we should be all set now. Quickly check the console. We don't see any linting errors happening here, so as long as you're running should be able to come over to your browser and now see that our author's page is rendering and showing Authors. So it worked. What we have right now works okay, but in the next module, we'll explore how controller views will give us some more maintainable and scalabe solution to this problem.
Great. We now have the Author page successfully displaying dynamic data from our mock API. Let's wrap up this module by reviewing some new concepts that we just put to use. Props give us an elegant way to pass data to child components, and they look a lot like HTML attributes. State is how we handle component data in our controller views. We use React state more in the next clip as we refactor what we just built into a controller view. And this will provide us better separation of concerns and reuse. The various lifecycle methods give us many options for bootstrapping our components. And it provide us clear hooks for integrating React with third party libraries. Now it's time to explore controller views. As you'll see in the next module, this pattern helps us create reusable components in React by separating our data marshaling and logic from our mark up.
In the previous module, we created the author page component. That component contains all the logic and markup for displaying author data. In this module, we'll learn about the importance of composition, and we'll see how to break our author page component down into the controller view pattern. Let's begin by discussing composition and controller views. Then we'll move on to PropTypes, which help us clearly convey the data that we expect to recieve in our child components. Then we'll wrap up by quickly discussing Mixins for handling cross-cutting concerns, which we'll put to further use in the following module.
React makes it trivial to compose components together. It's fundamental to the way that React scales, and the simplicity of composing components is one of React's greatest features. This simple example from the React docs is pretty hard to beat. As you can see, an avatar is simply composed of two other components: profile pic and profile link. You simply reference the child components within the parent component. Nice and simple. Also, there's an important concept of ownership at play here. The avatar component owns the profile pic and profile link components that you see here. By ownership, I mean the avatar component is setting the props for these components. Since a component can't mutate it's properties, they remain set to whatever value the owner sets them to. This leads us to the concept of controller views. To clarify, a controller view is simply a React component that has child components. At first, I found this jargon confusing. Why am I using the term "controller view" here instead of just calling it a React component? Well, controller views are a name for the top level React component. A React controller view controls data flows for all of its child components. It does this by setting props on child components. As you'll see in the flux module, controller views interact with flux stores as well. Don't let the name "controller view" confuse you. It's simply the top-level component on the page. You can nest components as deeply as you'd like, but it's recommended to have a single top-level controller view that interacts with the store, and passes all necessary data down to the children. Oh, and a quick note: it's possible to nest your controller views, but it's not recommended to do so, since it can cause multiple data updates. It can also cause React's render method to get called multiple times. Let's put this knowledge to use by refactoring the author page component into a true controller view. We'll delegate the markup down to a child component.
Demo: Controller Views
So now that we understand the concept of controller views, let's use that concept here to change the way that we handle the author page. Right now, the author page has logic that handles setting up state, but it also defines markup down below. Ideally, we'd have this markup handled in a child component called "author list", so let's create a new child component in authors, called "author list". To build out the basic structure for our author list, I'm just going to copy the author page and paste this in and then change a few things, because this is going to be our author list and it's no longer going to need a reference to the API, because the data's going to come down from the parent. We're not going to need "get initial state" or "component will mount". All we'll have is a simple render function that defines out markup. Since this is the author list component, I'm going to say "author list" here and here. Now, it will be receiving its data via Props instead. I'll be iterating over props instead of state. Remember properties are just passed down from the parent component. We can come over to the author page now, and the author page will no longer be defining all of this markup inside, we won't need the "create author" row anymore, either. Now, the author page will be making a reference to the author list component. We'll need to require this at the top. I'm going to make another change as well. Logically, this setting of state should happen in component didmount instead of component willmount. So I'm going to change that as well. There's one other best practice that we should follow, which is making sure that the component is actually mounted in this function, so I will say if this doc is mounted, then go ahead and set the state. Of course, down here we removed all the markup. Now, all we have is a call to the author list component, and we will pass that author list component the list of authors that sit within the state. So now, this entire component fits on one page, it makes it pretty easy to understand. The nice thing is, you can think of this as a separation between smart components and dumb components. This is the smart component; it's smart because it goes out, gets the data, sets it all up and then passes that data down to the dumb component down below, the author list component. Our author list does nothing but define some markup, and it receives its data via properties. As you can see here, it is expecting an author's property to be sent down to it, which we are doing on the author page right here. Before we can run this, I just realized I missed a curly brace right here. Now, we can just check the console to make sure that we don't have any errors, then go over to the browser, and we can see we have two headings here. Obviously, we need to go back over here, I've defined the heading in author list as well. This is a bit of a judgement call, but I'm going to place it only over here on the author page. So the author page has this H1. The list itself handles displaying the tables, so this can be a reusable table that we use in different places in the application. Good! Everything should be working now, and now we just see one header, like we'd expect. One thing that we could improve, though, if we come back over here to the code- Notice how this component expects there to be authors passed in on props, but right now it's not doing anything to make sure that happens. So in the next clip, let's learn how we can use prop types to declare our expectations for each component. That way, developers understand what data they need to pass in, to be able to interact with these components.
Prop types are a great way to be explicit about the data that you expect to see in your component's properties. You can specify that certain properties are required, and also specify the data types for each prop, such as a number, string, or bool. Prop types are simply a map that lets you specify a validation function for each property. When validation fails, a warning is logged in your console. This is an example of a prop type that we're going to set up to validate that the expected properties are being passed to our author form component. You can see that we are requiring an author object, requiring a function that should be called when the user clicks save, requiring a function that should be called to validate input declaring that any errors passed into our form should be of type "object", and requiring a function that determines if any errors have occurred. As you can see, you can add validation to require arrays, booleans, functions, numbers, objects, and strings. Now note, for performance, prop types only run in the development version of React. Think of this more as a way to document your expectation and catch issues during development, than as a way to enforce these rules in production. Also remember: every prop type that isn't required should have a corresponding field in get default props. I just mention development versus production modes, so I'd better clarify this. Prop validation, as I said, only runs in development mode, so you can't rely on prop validation in production. To clarify, the minified version of React runs in production mode by default, so be sure that you deploy the appropriate version of React to each environment. Keep in mind that prop validation won't end up running in production. So let's check out a demo of declaring property types in our application.
Demo: Prop Validation
So in a previous clip, we refactored author lists to be a separate component that is composed under author page, so author page is our controller view, and author list is composed right here. However, what we'd like to do is declare the author list always requires authors as a property. We can do that using prop types. So let's define some prop types. You do that by just saying prop types, and then declaring your prop types right here, within a static object. We would say authors React.propTypes.array.isrequired. So this is saying that this particular array is required, and if it's not passed in, then we will be notified. We can go back to our application and see that it continues to work. We can also check console and see we're not seeing any errors here in the console. But what if we came over here to author page, then, and we didn't pass in authors; if I just delete this and just try to call author list without the props that were just required, then what happens? We don't see anything in the console, but if we come over here to the browser, well now we don't see anything in the browser. We can check down here and it says "required prop authors was not specified in author list. Check the render method of authors". That's a very helpful error message. Now we know that we need to go look at the render method of authors to find our mistake. Here's the render method within authors, again this is called authors because I called it this. In retrospect, I probably should call this "author page", and we'd get a little more helpful message. Keeping a one-to-one relationship between the name of the file and the variables that you set here will help you get really useful error messages. So let's just go ahead and do that. We'll call this author page here, and author page here, hit save, and now we'll come back over into the browser and refresh, and now we see check the render method of author page. That's an even more helpful error message. It's a good convention to follow: keep your file name and the variable that you export in sync. We can see now that the prop types that we declared right here gave us this useful message. If I came in and commented this out, and then I refresh the browser, then I no longer get a helpful error message; I just get "cannot read property map of undefined". Of course, that error occurs because right here, I'm trying to map over some data that doesn't actually exist. This array author is null, in this case, because I'm not initializing it anywhere within this component. We'll go ahead and turn prop types back on, and we will pass authors back in, like we were doing before. Now there's a lot of different prop types that you can declare that we saw in the previous slides. Prop types, in short, are just a handy way to declare expectations for a given component. So far, our app's running great. We can navigate through to the different pages, we're seeing a list of authors so we're interacting with our server, but there are some issues here. We have some issues with routing because if I click on these links that I created in an earlier clip, what you notice is that we end up back at the home page. It's not honoring this second segment in the URL. That's because we really hacked in routing for this first module. There's some other issues with it. If I click "home", for instance, do you notice how we get a complete post back, even though we were trying to build a true client-side application. There's also issues that we've hardcoded in links in various places, where we'd like to have just referenced a variable, so we could centralize all of our links. What if we'd like to interact with the query string and use that to help pass data to components or reroute us to different areas of the application? That's not easily possible, given our current adhoc router setup. So in the next module, we'll pull in React router and see how it cleans up our current routing configuration, and gives us a nice, scalable architecture for handling routing within our application.
Mixins are a handy way to handle crosscutting functionality. In other words, Mixins are useful when you have code that you want to be able to run in a number of different components. You can declare them by setting the Mixins property in your React component, and populating that property with an array of Mixins like this. We'll check out an example of Mixins when we explore the React router in the next module.
Let's wrap up this module by reviewing the core API for react that we just discussed. We learned that controller views are "Smart" components that pass data down to their children via props. We refactored the monolithic author page component so that it passes it's data down to child components via props. We called this child component author list, so author page is now a proper controller view. We use PropTypes to specify and validate properties during development. You can specify data types and whether the types are optional or required, but remember it's only run in development mode. Finally, we breifly touched on Mixins. Mixins are a handy way to handle cross-cutting concerns in React. When you have some behavior that you'd like to use on a large number of components mixins give you a simple way to share that behavior across many components. We'll put mixins to use more in the next module on React Router since it ships with some handy mixins right out of the box. So now it's time to replace our custom routing code with something more scale-able and elegant. So in the next module we'll begin implementing React Router.
Hi, I'm Cory House. In the previous module, we saw how React components give us the power to create rich web UIs using small, composable components. But now, we need a way to route between the different parts of our application. So to get that done, we need to select a router. In this module, we'll implement the most popular routing solution for React called React Router. Since React is a small, focused view engine, it has no opinion on how to handle routing built-in. And for small apps you may have no need for a fully-featured router. But as your application grows, you'll likely find you want to split the application into multiple client rendered pages with deep linking. And that's where a routing library comes in handy. So for routing on our app, we'll use React Router. React Router offers a simple, nested approach to routing. You declaratively configure your routes and the ability to nest routes means that it can support complex UIs that are many layers deep. And React Router isn't just used at Facebook, it's also supported by Facebook employees full-time. Finally, if you're already familiar with Ember, you'll find React Router is very easy to pick up since it's heavily inspired by Ember's approach to routing. Regardless, I'm sure you'll find the declarative and centralized approach easy to understand.
React Router provides rich route configuration options. You define your routes using the route component. You typically define a route for each page in your application. You can declare a default route which is what displays when someone loads your app at the root. And you can declare a component to handle a client side 404 page using the not found route. You can also programmatically redirect to another route using redirect. We'll put each of these to use in our application. All right, it's time for a demo. Let's set up our application's initial routing configuration using React Router.
Demo: Route Configuration
To get started with React Router, I like to begin by defining my routes in a centralized route file and I like to call that routes dot js. I think it makes sense to put it under the root in source so we'll create a new file. I'm going to save that as routes dot js. And now within here, we'll start as usual, our use strict. And then also, we'll need a reference to React. In addition, we're going to need some specific router functions. We're going to need a reference to the router. So I'll reference React Router. And then I'll also need a reference to a default route, this will be used so that we can declare what route should load when the page loads without any other segments in the URL after the initial slash. And finally, we'll need a reference to the route component which is used to define our routes. Now that we have our core dependencies, we can define our routes. And I'm going to define my routes right here. And I'm just going to paste in our initial list of routes. But you can see as I use the route component and the route component contains a number of routes inside of it. We have a default route here which defines our home page. So if I just go to the root URL then I'm going to load the home page component. I also have routes defined for the authors page and the about page. As you can see, I just say, "Here's the name of the route "and here is the component that I'd like you to run "when this route is called." Now you might be wondering, how the path is handled here. Well, there's an interesting convention going on. If I don't define the path then React Router just assumes that the name is also the path. So slash authors will load for this route and slash about will load this route. If I wanted to define a route here that differed from my name, perhaps I'd like the URL to look like this, about page or about dash we then about dash us would be what sits in the URL and the name is a separate convention that we'll use here in a moment to be able to reference this route throughout our application as we link to these different routes. But typically, I find it useful for the name and the path to match so I'm going to leave the paths out here except for this spot where we do define the path for the base route to our app component. And I'll place a semi-colon here at the end, close our routes. And obviously we will export our routes from this file. So this is our initial set up for React Router. This defines our routes. And now we can jump over into main js and begin taking out the custom routing code that we had before since we can lean on React Router to make our routing configuration simple and declarative.
Now that we've defined our routes, we can go over to the main js file and begin simplifying this file greatly because we no longer need all the complexity of handling routing on our own. To start off, let's take the app component that we were declaring inline and instead, move it out to its own component that sits over here with the other components that we've already defined. So to do that, I'm going to cut this and then create a new file under components and we'll call this component app dot js. So this is the highest level component for our application. We'll add in our use strict. And also add a reference to React. And then I will just fix the indentation. And of course, add an export for the app. The other things that we can get rid of are our routing code that sits here because now React Router is going to handle this piece for us. This makes our app component very, very simple. Of course, we do need references to the header so let's go back to main js and remove that, I'm going to paste it here. Now the question is, what do we do here? Before, we were handling our own child routing through our custom routing solution. Now React Router is going to handle this for us. I'm going to remove the child handler and instead put route handler here. And at the top, I'm going to define a reference to route handler. This route handler is part of React Router. All right, let's step back over to main js and see what else we need to bring over. We no longer need these references to home, authors and about because those are handled in our routes file which declares the references to these components. And we also don't need our custom rendering code anymore. And of course, this IIFE was only useful because we were trying to keep ESLint from getting cranky because of this global variable so I'm going to show a different way to handle global variables in a second. Let me take this out for the moment. And now we've completely streamlined main dot js. Now, I do need to add a few things back in. We need our reference to our router. And we also need a reference to our routes. Now we're ready to run the router. To do that, we say router dot run, we pass it the routes and then we pass it a function. And that function ends up taking the handler. And then inside, we call react dot render. And we pass it a reference to the handler. Finally, this should look familiar. Document dot get element by ID, app. So this is referencing the same app placeholder that we've been using before that sits within index dot html. That's where our react application will be hosted. And that's it, that's all we need in our main js. So we pretty much deleted everything that we had before from main js and now we just have this little bit to run the router and then render the application for whatever handler is currently in scope based on the URL. Now I did mention I was going to handle this global variable for jQuery a little differently. Again, the only reason we need this is just to make Bootstrap happy. I'm going to take it out of here, add my use strict in and then move it over to app js because that just feels like a more logical place for me to do this. And then in this one file, instead of calling use strict, I'm just going to disable the check for use strict on this particular file. This is just to show you another approach that we can use to keep ESLint from getting cranky in this one spot where we have a global defined. So a couple different ways to handle it. I could also just end up disabling ESLint around this one variable if I chose to go that route as well. But now we can see we have a nice, concise app. And our app has a header and it has a route handler. And then this route handler will end up rendering the appropriate child component based on the URL. React Router will take care of all of that for us. And if we go over to our command line, assuming that gulp is still running, we can see that we have one more error to take care of, it can't find the header module. So let's go back to here and fix our path. When we moved it over to this file, we needed to take out this one directory reference. While we're here in app dot js, I'm going to add a wrapper around our route handler just so that we get a little bit of spacing around it. This is really just a cosmetic issue but this will make the application look nicer as we move forward. So we'll wrap this in a fluid container which is just a class that comes from Bootstrap. Now that this is all set, we can jump over to the browser and see that it's all still rendering just fine. Great, it worked. That was pretty easy. And now the application's configuration is much cleaner. We have better separation of concerns and we no longer need to manage a switch statement as we add new routes. And as you'll see throughout the rest of this module, we also now have the power to easily handle complex URLs and query strings. And another nice thing that we can do is centralize all of our management of these different links which we'll get to see in the next clip as we talk about the link component that ships with React Router.
Params and Querystrings
Once your application gets very complex, you'll likely want to pass parameters in the URL. React Router automatically adds this data to props under params and query so let's take a look at an example. Given a route that you configure this way. As you can see, the course ID is declared as a placeholder here in the middle using a colon and then the name of the placeholder. And a URL that looks like this, so you can see I'm passing module as a query string with a value of three. Then the components props will end up being populated and accessible like this, so you can see that I would say, this dot props dot params dot course ID to be able to get the course ID from the route. And then to get the query string, I would say, this dot props dot query dot module and that gets me the value of three. And of course, this dot props dot path is also populated for me and that would give me the full path in the URL. We'll see a demo of working with params and querystrings in the next module when we work with React Forms.
React Router offers a handy abstraction over anchors called link. Link allows you to leverage the routes you've defined so you don't have to repeat them every time you want to link to a given page. Let take a look at an example. Image we want to create a link to a user with an ID of one. We'd likely want a URL that looks something like this. To support this URL, I define a route that has a placeholder for the user ID. So that colon user ID at the end is a placeholder. Also note, that I've given this route a name of user. This gives us a handy hook for referencing this route in our links. Now to create links that point to this route, we can write some JSX that use the link component which is provided by React Router. We set the to property to user because that's the name of the link that I want to link to. Params let us define a JSON object of data that's useful for the URL. The properties should correspond to the placeholder you set on the route. So note how the user ID in the route and the link correlate. And ultimately, when the JSX is actually compiled, this normal hyper-reference will be generated. As you can see, it's just plain HTML and it has the appropriate hyper-reference and body. Again, the big win here is it's common to create multiple links to a given resource in your app. This feature effectively normalizes your links by abstracting away hyper-references and instead, it lets you simply reference your routes by name. Very handy. Let's create some links now using React Router.
React Router also allows us to effectively normalize our references to every hyper-reference in our application. That sounds kind of confusing at first but this actually quite a simple concept. If I go over here to components and I open header dot js, look at what we've done right now. With our previous routing config, I had to hard code in these links. Now you can imagine, in a larger application, I'd have to carefully hard code in these links in a number of different places. And if we ever changed a URL then that could come back and bite me. It'd be nice if I could just reference one of the routes that we already declared in our routes config. And that's exactly what we can do using the link component that comes with React Router. We'll start by making a reference to React Router. And then we'll get a reference to the link component. And now we have what we need to create our links down below. To change these to links, it's quite simple. I just use link instead of anchor and instead of hyper-reference, I make a reference to the named route that I'd like this link to redirect to. The named route for home is just the app, the high level app. And of course I change my closing tag as well. And I'll go ahead and do this for our other two tags also. So this will end up being a link to authors and this becomes a link to about. So you can likely see the benefit here, not only am I avoiding repeating myself but it also reads quite nicely. I just know that I need to go redirect to this named route when this link is clicked. And if I go to routes, I can see the name was authors, the name was about, the name was app. So I'm just using these names over here in my header. I can also update this link which is used for the logo. And then of course, I need to point this to app as well. So now we've converted this file over to use the link component rather than traditional anchors throughout the file. It's just using our routes dot js to determine the proper links. Now that this is all wired up, let's jump over to the browser and see how it works. As you can, all our routing still works as expected but there's one nice benefit to us using that link component instead of the hard coded anchor. What we can see is, when I click on the home page, it no longer post back, we don't see that flicker that was happening before because React Router takes care of that and avoids a full post back. And now that we have this set up, I'd like to emphasize a little more of the usefulness of the link component by using it in another spot over on the home page. Image that I'd like to have a call-to-action underneath our banner. And that call-to-action is a link to the about page. I'm just going to style it using a little bit of Bootstrap here. And then have some text that says, "Learn more". Now of course, to make this work, we'll also need to add references to React Router and the link component. Now that this is wired up, let's see how this looks. We can go back to the home page. Now we can see that we have our call-to-action sitting here underneath our banner. When I click it, I'm taking to about just like we expected. But the big win here is, this link is to about and this link is to about but I didn't have to hard code in multiple references to this specific link. So later, if we decide to change about to about dash us, for instance, I have a single spot in the application where that's normalized, that all sits within routes dot js. This makes large applications easier to maintain and grow over time. So I recommend using the link component when you're working with React Router. So what if somebody enters a URL that's no longer valid? Well, in the next clip we'll see how to handle 404s by defining a not found route.
There's another important concern that our old, hacked in routing didn't handle well. What about 404s? When someone enters a bogus URL, it would be nice if our application showed a friendly error page. Let's make that happen. First, let's create the page that we want to display. I'm going to put it under components. I'll save this file and call it not found page dot js. Or maybe you'd like to call yours 404 dot js. And to save you from seeing me type, I'll just paste in this component. We can see, we're using the link component again because we're going to have a link back to the home page. And then we just have a small message that there's nothing to see here and the page wasn't found. This is a pretty standard 404 page. And now that we've created this, we can go over to routes dot js and define a not found route. To make that happen, we first need to get a reference to the not found route component that comes with React Router. Now that we've referenced the not found route, we can go here and use the not found route component. We don't need to define a name for it but we do need to define a handler. And that handler will look like our other handlers but of course, we'll be referencing a different component. Our component in this case is the not found page. And assuming we wired this up right, we should be able to alt tab over to our browser and if gulp is running then our browser should already be updated. You can navigate around as normal. But if I go in here and put something bogus on the end of the URL and hit enter, we get our not found route. And we can see our 404 page rendering as expected and it also has a link to take us back to the home page. So this gives us a simple way to handle 404s while working with React Router. Of course, another nice thing to do is to redirect from old URLs so that people don't end up at a 404 page. So in the next clip, let's see how we can handle redirects.
So what if you decide to change URLs at some point? If you want your old URLs to continue to work, you'll want to create a redirect from the old URL to the new one. That's really simple to do with React Router. Simply alias redirect. In other words, create a variable like this so that you can reference it easily as a tag when declaring the redirect. From is the path that you want to redirect from, this includes any dynamic segments so you can use an asterisk to redirect anything not found somewhere else. To is the name of the route that you want to redirect to. You can also specify params and querystring parameters if you like but that's typically not necessary because they'll pass right through to the new route by default. Let's try this feature out by creating a redirect in our application.
As applications grow and change, it's common to retire old links. We need an easy way to redirect people from an old link that's no longer valid to some new page and we can do that quite easily with React Router's redirect component. We can also use the redirect component to handle common typos in our URL. So first let's use the redirect to handle bad references, references to a page that we've since changed. To do that, we'll need a reference to the redirect component. And then we can define our redirect right down here along with all of our other routing rules. In this case, we will redirect from... Let's say we used to have a page called about dash us. And we decided to simplify it to about. So this rule will take any requests to about dash us and redirect them to about. Let's save this and see how it works. So we'll switch back over to the browser. And let's try to go about dash us. And when I do, you can see, I end up at about just like we expected. Of course if type in something else, then I'll still end up with the 404 page because we don't have any redirects for about 33 which I typed in this case. We could also choose to go back into here and create another routing rule. And for this one, maybe I want it to handle typos. Maybe someone is confused about how to spell tricky words like authors. And with this rule, we could now come over here and type authors the wrong way and we see that we land at the right page. So this is a nice way to handle any common typos that might apply to your business. And let's check out one final example that can be useful. What if we would like to come in here and create a rule that redirects for sub-directories? Perhaps, before we had some pages that sat under about but now if anyone tries to request a page under about, we'd just like to redirect them to the about page. So you can put wild cards in your redirects. Let's see how this works. So now, if I come to the about page it loads fine and if I try to load anything under it, it's just going to send me back to about. So that's three ways that React Router's redirect component can be useful for helping improving the experience for your users.
When handling transitions between components and pages, it may be helpful to run code before loading and unloading. That's what the static functions will transition to and will transition from are for. Will transition to is a useful place to check for any rules that determine whether the page should be transitioned to. So for example, if you have a page that requires the user to be logged in then will transition to is a good place to check if the user is authenticated. If the user isn't authenticated, you may want to abort the user transitioning to the page. Will transition from is useful for any checks that need to happen before the user navigates away from the current page. A common use case for this is checking if a form has unsaved data so you can prompt the user. This helps avoid navigating away and losing work. Here's some example code. As you can see, will transition to is checking whether the user is logged in and it's aborting the transition to a protected page if they're not authenticated. And will transition from is being used here to warn a user that they have unsaved changes that will be lost if they navigate away. So these are two useful functions for controlling whether transitions can occur between two different routes within your application. And all of this occurs on the client side using React Router. Now let's put will transition from to use. We'll use it to keep people from losing their work when they're editing our author data.
Sometimes we need to control when the user can navigate to and from a given route. For example, if part of an application requires authentication then it's useful to check authentication before the page is loaded. And if the user isn't authenticated, you'll likely want to redirect the user to the login page. And in other times, we may want to stop the user from navigating away, such as when a form is partially completed. Let's create a simple example of handling transitions to and from a page using React Router. What I have open here is the about component. And using React Router, we can just define a couple of statics, these are static methods. And they have magic names which is will transition to and will transition from. So let's start with will transition to. And this is a function with a rather long signature. You pass it a transition, params, query and a call back. We'll see how those are useful here in a moment. And inside of here we add our logic to determine whether this page can be transitioned to. If we had a login structure, this is where I would say, if you're not logged in, maybe I'd require you to log in before you view this page. But since we're not handling logins on this demo, let's just do something simple. I'm going to ask a question and a confirm. "Are you sure you want to read a page "that's this boring?" I mean honestly, there's not a lot going on on this page. Might be worth asking people. And then based on their response, if they answer this, they're not so sure then let's go ahead and abort. Otherwise, what we can do is call the callback. Calling the callback allows the transition to occur. If we didn't do this then the transition to the about page would never happen after I clicked the link. So we'll save this and then let's see how this works. And checking the console, it looks like we have a typo. I am missing a comma right here after I declared my static. So let's save that, see whether that makes the console happy. Looks like it did. And now our app's loading again. So let's go over to the about page. And that's odd, it should have prompted us when I clicked on about so I must have a typo in my code. And it is right here on static, this should be statics, plural. It needs to be called statics, plural, so that it gets attached properly by React, React looks for this object. And oh, we can see, right down here that now it is prompting us. So if I go to home and then click on about, I get my nice little message here. Am I sure I want to read a page this boring? Ok, I still will. If I go back to home, I click it again and I hit cancel because now you've warned me, yeah, ok, I'll stay away from your boring page. Then I just stay here on the home page. So that's how we handle transitions to, now let's look at an example of transitions from. We'll go back here and I'm just going to copy this because transitions from are fairly similar, we can save ourselves some typing. Obviously, this will say will transition from. And the method signature is simpler here, it's just a transition and a component. And in this case, I'm going to be bipolar and I'll say, "Are you sure you want to leave "a page that's this exciting?" And really, now that you're here, you might as well stay. And we don't need the rest of this either, there's no callback to call. So we'll save this. One other detail we need is a comma between these two. And now we should be all set to see how will transition from works. Go over here and there's our message for will transition to. Do I really want to hit a page this boring? Yes, I do. But now, when I navigate away, it prompts and says, "Are you sure you want to leave a page "that's this exciting?" Now of course, this is a silly example. This is an odd thing to do but you can imagine how both of these could be useful, especially when we're working with forms and we want to avoid people losing their work. So in the next module, as we work with React Forms, we'll see how this is useful on our author form that we create. And before we close out this module, let's check out how we can create clean URLs by changing the configuration of React Router.
Perhaps you don't like having these hashes sitting up here in the URL. No problem. There are alternative history approaches that ship out of the box with React Router. We're using the default which is hash location style. This style provides broad browser support, however, if you'd like cleaner URLs, you can easily switch to using one of React Router's other location styles. We're going to look at an example of using the HTML5 history style instead. Let's jump into the code and make a simple one line change to make this happen. To make this change, we'll go to main dot js where we're calling router dot run. There's a second parameter that we can optionally pass here. And if we'd like to use the history location style which uses HTML5 push state to change your URLs then we can just add this one extra parameter. And that is router dot history location. And with that one line changed, now we should be using HTML5's history API so React Router will be using push state, replace state and pop state to change our history. So let's see if this works. As I click around now, I end up at home, authors, about. I still get our annoying little nag screen there. But now you can see that the URLs are clean, we no longer have those hashes sitting within the URL. And we can of course, go back because it is just pushing into the browser state. So we have all these options for going back and forward through our application. So this feature allows for clean client side rendered URLs instead of the hash based URLs that we've been using so far. But note, this clean URL feature doesn't work on older browsers like IE 8 so be sure to consider your browser requirements before making this change. And there's one other trick if you go this route. You have to configure your server to support clean URLs. Basically, you need to direct all requests for your app to your client side index page. This way, React Router can take over routing from there. Finally, the current 1.0 beta version of React Router is very similar to what we've learned in this module but this is one area where the API has changed so be sure to check their docs for your particular version. The bottom line is, since using clean URLs requires extra work on the server, we're going to keep things simple for the example app and just stick with hash based URLs for now. So I'm going to back to the code and back this change this out. For the rest of the course, we'll just continue to use the hash based but I wanted you to be aware of the alternatives.
I briefly introduced the concept of mixins in the last module. Mixins are a handy way to handle cross-cutting functionality. In other words, mixins are useful when you have code that you want to be able to run in a number of different components. You can declare them by setting the mixins property in your React component and populating that property with an array of mixins, just like this. So here we're using the router dot navigation mixin and the router dot state mixin. These are two mixins that are included in React Router. React Router provides a navigation mixin because it's useful when you want to programmatically move to a new route. Once you reference the navigation mixin within your component, you can do things like transition to a new route programmatically, replace the current route with a new route that you reference by name, you can go back to a previous route or you can also create a URL to a totally new route by using the make path function. We'll see a demo of using the navigation mixin in the next module as we work with React Forms.
Let's wrap of this module with a quick review. As you can see, React Router offers a low friction way to set up client side routing. It's custom built for React and it shows. React Router makes it easy to declaratively define routes so that you can envision how your routing will work by simply looking at the code. Parameters in the URL automatically populate corresponding props on your React component and you can easily access query strings programmatically as well. The link component that ships with React Router effectively allowed us to normalize our hyperlinks. With the link component, we can easily create hyperlinks by simply referencing a route by name. We saw how will transition to and will transition from gave us an easy way to control transitions between pages. This is useful to keep users from navigating away and losing work and also to help ensure that authenticated routes properly require access before loading. Finally, we saw how the navigation mixin can be used to programmatically transition to new routes, replace the current route or go back to a previous route. And now that we've completed our applications routing, it's time to shift our focus to handling data using Flux. In the next module, we'll explore how unidirectional data flows make our application predictable, fast and easy to debug.
So far, we've learned how to create reusable components with React, and we've seen how to handle client-side routing using React Router. Now it's time to enable writing and editing author data using forms. As you'll see, React's virtual dom makes working with forms a bit different than what you might be used to in Angular, Knockout, Backbone or Ember. We've been doing forms for years, so forms should be easy, right? Well, if you've built forms for long, you have likely learned that there's a lot of pieces to get right. And watch out, because React has some unique attributes that might surprise you when you start creating forms with it. In this module, we'll implement a form to handle adding and editing author data. This sounds simple enough, but we'll cover a long list of concerns, including validation, redirects, reusable inputs, user notifications, and saving and populating data on load. You'll learn when and where to wire these up, and some best practices for creating forms in React. Bottom line, forms can be complicated, but this module will set a clear path for implementing robust, responsive forms that make users happy.
Create Manage Author Page
To get started working with forms in React, let's create a page where we can manage author data. So we'll go over to the Author folder, create a new file, and call it manage author page.js. I'm just going to paste in an initial shell here for the Manage Author page. And I'll put a simple header here just to get us started. Of course, with the new page, we also need a route so that we can get to this page, so let's go over to Routes and paste in. This route should look pretty familiar to you by now. Just an add author route, but we're going to call the path, in this case. In other cases, we haven't put in a path because the path and the name matched, but in this case, we'd like the route to be named add author, and we'd like the path just to be author. So it will just be slash author that gets us to the Manage Author page. And of course, we're referencing the Manage Author page component that we just created. We'll go ahead and save that. The other piece we need is a way to navigate to this page, so let's go to the author page and add a link in to the Manage Author page. Of course, when creating links, then we will need React Router and the link component from React Router, so we'll add these at the top. We'll also need a link down here. This is where we will link to the add author page. I'll just paste this in, cause, again, this should look familiar by now. Using the link component, we're linking to the add author route that we just wired up in routes.js. Then we're using just a little bit of Bootstrap to make this link look like a button. Okay, this should have wired up our new button, and we now have a page that we should be able to navigate to. So let's go over to our browser. We can see the Add Author button sitting here. When I click it, we're taken to the new Manage Author page. Pretty empty at the moment, but this is a good start. With this foundation, we're now ready to wire up the Manage Author page controller view.
Build Controller View
On the previous clip, we created the Manage Author form, and as you can see, the form loads fine and it looks decent. However, check out what happens when I try to type in one of the fields. Can you hear my keyboard clicking? But I don't see anything. Even though I'm typing here, my input doesn't seem to be registering. To understand why, you have to remember the discussion that we had on the virtual dom in one of the previous modules. Remember, React automatically redraws the UI on every request animation frame. Since we haven't attached any change handlers to these inputs yet, there's no way for their value to change. So even though I'm hitting keys, the value is immediately lost, since React ignores it. Now, this likely seems odd, especially if you're used to two-way binding frameworks like Angular or Ember. But don't worry, you'll get used to this quick, and explicitly creating your change handlers has some benefits, as well. To get these text boxes to register input, what we need to do is assign a value to each text box and create a change handler, as well. Let's jump back into the code and set that up. First, let's create some initial state for this form in the controller view. Remember, the controller view is the top-level component, which, in this case, is the Manage Author page component. We'll create a get initial state function right here. I'm just going to paste this in. What we can see is we're defining the initial state to contain an object called author that has three properties, ID, first name and last name. Now that we have this state defined, we can pass it down to the child component via props. So let's go over here to the author form component, and then we can pass in our author data. So this passes our author state down to the child component. This will show up in props within our author form component. So now we simply need to update the author form component to utilize this data. So instead of these blank values that we have now on these text inputs, we can reference the properties that are being passed down. So this first name text input will now be bound to the first name property, which was passed down from the parent component, Manage Author page. I'll just change this to last name. Now that we're passing down the initial form state via props, we also need to set up change handlers for these inputs. To do that, let's go back to the Manage Author page. Again, since this is the top-level component, it's the best place to handle changes to state. So let's create a set author state function. Our set author state function can sit right here above render. As you can see, it has four steps. We're going to take the event that occurs, and then use that to get a reference to the field and to the value. So for instance, the field might be the first name field and the value would be whatever value has just been typed in as a key was pressed. Then we're going to update state for the field that was passed with the value that was passed. So these are just shortcuts to what we're using here on the third line. Then finally, we have to call set state to actually update the state. So we say this.set state, and the state that we want to change, in this case, is the author object. We set that author to this.state.author, which was set on line 16. So this is a common pattern that you'll see to be able to update state that is getting bubbled up from some child component. So with this function, when I type a key in the form, this function will be called every single key press that I make. That way, the state will get updated on this component. Of course, that won't happen until we pass a reference to this component down to the child component. Of course, to make that happen, we'll just need to add another item down here to pass to our author form. So now we're passing both the author and a change handler, which, in this case, is pointing to set author state. So we're saying when anything changes within the author form, we want this function to run. Of course, now that we've wired up an on change handler right here, we also need to use it in the child component. Let's go back to the author form and add this in. I'm going to add a change handler for both of our inputs. So we can see, we're just using the change handler that's getting passed down from the parent. We, again, just use this stop props.on change. Then this attaches a change handler for our input. So this will end up bubbling up to our parent component and calling that change event that sits right here. All right, so now that we've wired up our state and our change handlers, we should be able to go back to the form and have our keystrokes. Yep, our keystrokes are working now. We can see them reflected as I type. Now, I know you might be thinking wow, that was a lot of work. I agree. At first, it certainly feels that way, but there are a number of ways to streamline this process if you like. That said, both React and Flux have a clear preference for clarity and scalability over convention and minimizing typing. This is a common trade-off in many frameworks. That said, there are certainly ways to enable traditional two-way binding in React if you miss it. I'm just showing you a simple, common approach for getting started here. If this level of explicit state management becomes a pain point for you, rest assured, there are alternatives. Speaking of pain points, let's jump back and look at the inputs that we've created because there's a way that we can dry out our code and eliminate a lot of the duplication that comes with instantiating multiple inputs.
Creating Reusable Inputs
Let's go back to the author form that we created. We can see that there are some potential issues. As you can see, there's a lot of duplication here. I had to manually keep the label and input in sync. I had to provide the same class name to each input so that Bootstrap will style it properly. I entered a placeholder that matches the label text as well. Now, that's admittedly redundant, but it's a nice touch. I also added this ref attribute, which is occasionally useful when you want to get a reference to a child component. This value is typically passed to React.find dom node so that you can get a reference to the component. Finally, of course, we define the actual value. The wild thing is, this isn't even all the markup that we're likely to want. To fully style these components with Bootstrap, we'll want to create div wrappers around each of these fields. We'll also want a consistent placeholder for displaying an inline error message. Add all this up, and it's clear. What we need is a reusable component for text inputs. So let's create a text input component that can handle all of this complexity in a single spot. To get started, let's create a text input component under Common. So I'll save this as text input.js. To get started, I'll paste in just a shell using the name input and exporting that module. Then let's paste in the initial markup that we'll want to use for a single input. And wow, that's a surprising amount of markup. This really emphasizes why we want a centralized text input. As I said, when we're working with Bootstrap, there's often some extra classes and divs that we need to style and manage the look and feel of our components. So in this case, I have a class name on the outer wrapper, and we're going to need to write a little bit of code that sets this wrapper class. We obviously need a label for the input, and then we also have a wrapper for the field itself, which Bootstrap likes us to provide a class of field, in this case. Then we get down to the native HTML element, which has a name, a class name, a placeholder, a value, a change handler. Then finally down here, a placeholder for errors as well. So this is a lot of configuration, but the nice thing is this will now all sit in one place, and we can just pass data down to our inputs. Now, of course, when you build apps in the future, your needs may differ, but the nice thing about centralizing your text inputs this way is you can decide how you're going to handle text inputs throughout your entire application, and then this becomes a centralized abstraction for that decision. So this doesn't just make it easier to create a form, it also enforces the conventions that you've agreed upon. So I mentioned that we needed to write some code to calculate the wrapper class, and we'll get that done right here inside the render function. We're just going to paste this in and then explain it. What we're doing is just dynamically creating a wrapper class, and we're starting with form group, which is another Bootstrap class that we'd like to wrap all of our inputs. But then if there's an error, then we'd also like to add the has error class. The has error class will add a red line around the input to draw attention to it any time there's an error. Again, this is a class that is provided by Bootstrap. So what we're really doing here is just concatenating strings so that we can dynamically create a class name. There are some slicker ways to get this done, if you check out on GitHub, but I'm just going to go with the simple approach here. There's one final piece missing. Anytime we create a reusable component like this that we expect to be used by a lot of different people, it's helpful to define PropTypes to make sure that people understand what data to pass down and also so that we get warnings if we don't pass the proper data down. So I'm going to define the PropType up here above the render function. What we're saying here is that these first three are required. We expect to always get a name, a label and change event, and that optionally, we're expecting to get a placeholder that's a string, a value that's a string and an error that's a string. But these first three items are required for anybody to use our text input. Now that we've finished creating our text input component, we can go back to the author form and put this component to use.
Consuming Reusable Inputs
In the previous clip, we created a reusable input component, so this abstracts away text inputs into a centralized component that we can reuse. Let's go back over to the author form so that we can put this text input to use. To get started, I'm going to add a reference to the input component here at the top of the page. Then here comes the real striking part. I'm just going to paste over what we have here now with the new streamlined version to show how much we've simplified this application. Wow, now that's a striking difference. Let's look at some of the things that just changed. We can see how things are significantly shorter. What we got rid of was, see how we have a label and an input here? We no longer need separate labels and inputs because the input tag alone handles both of those. We're making the assumption that if you're using the input tag, you're going to need a label to go with that input. We can also see we're no longer having to pass in a separate ref parameter. That's just handled as part of the name. By convention, ref and name will be the same, so we've just created a nice convention to save ourselves some typing. You can also see that here in the previous setup, I had to define the form control class for each of my inputs. I need to remember this or they wouldn't be styled properly. We no longer have to define that because the form control class is now defined within the text input. And although you can't see it here because we didn't have the wrappers in this version, our new text input also put the necessary div wrappers around each of these inputs, which will help us in a moment when we wire up validation for these inputs. So let's save this change and let's flip over to the browser and just make sure everything still works. Our inputs still look the same, and they still take input, just like before. So it looks like we haven't regressed anything, but now we have these handy, reusable text input components that are going to enforce consistency throughout our application and save us time as we configure different inputs as this application grows. Now it's time to make this Save button actually work. So in the next clip, let's wire that up.
In the previous clip, we got the Save button working. However, the experience isn't very good right now. You hit Save and you don't get any kind of feedback, and you just continue looking at the same page. So for starters, let's wire up a transition using React Router. The first step in this process is to add a reference to React Router. So we'll add it in here. And to programatically redirect the user, we will use the navigation mixin that comes with React Router. So I'm just going to add a new line and paste in a reference to this mixin. Remember, mixins are just defined as an array, and by convention, that array needs to be called mixins. That's what React will look for. The mixin that we're going to use to programatically navigate the user is the navigation mixin. So we've referenced it. Now that we have, we can go down there to the bottom where we are saving the author, and after the author's saved, we can redirect the user. To do that, I'm going to call this .transition two. Then I'm going to pass it the named route that I'd like it to transition to. In this case, we want to go back to the Authors page, so I'll just type authors. So this is saying transition to Authors after a save occurs of above. So I'll save this, and now we should be able to try again and see if our redirect works. Let's go create a new author. I'll put in John Smith again. And click Save. There we go, redirect worked. Now we've come back to the Authors page. We can see the new author that we just saved, as expected. However, this could still be nicer. We're not getting any kind of a confirmation that it's saved. I had to look down here through the list to know that my save worked. So let's pull in a library to give the user a notification after the save completes.
Right now, it doesn't matter what we enter on the form, it will accept it. I could go over here to add an an author and I could just hit Save on an empty author, and we'd get nothing here. I could enter just a single letter, and we get AA. There's probably some rules that we should put around this since an empty author name doesn't really make much sense. So let's add a little bit of validation to our form. The save author method's the logical place where we should be calling our validation, so I'm going to put a check right here that says if the author form isn't valid, we should just return. There's really nothing else to do here. So let's define the author form is valid function right up here. This is where we'll be performing the validation for this form. Let's create a variable that just keeps track of whether the form is valid. We will default to true. We'll also want to keep track of any errors that occur as we validate this data, so let's use state to keep track of that. I will say this.state.errors equals an empty object. I'll just re-initialize so that I can clear any previous errors in case somebody submitted the form multiple times. Now, since I just defined state here, we should also initialize it up here at the top in our get initial state. And I'll need a comma right here. Okay, now that we have our state initialized, let's move back down here to our validation function. I'm going to do really simple validation here. I'll say this.state.author.first name .length We'll say if it's less than three characters, then something's wrong. So here's where we would set an error. I'll say this.state.errors .first name. Then we'll put our message in right here. I'll say, "first name must be at least three characters." So this is a really simple piece of validation, and in fact, I'll just copy this and we'll assume that we're going to do the same thing for the last name, so I'll just switch this up. Now, of course, you'll want to do something more scalable, but I just want to keep this example simple so that you can see how you can get started doing validation in React. So now we potentially have some errors, and we need to save those to state. So I'll say this set state errors, this.state.errors. Remember, anytime you're going to change state, you have to call set state to do so. Finally, we can return whether the form is valid. I just realized one other thing we should do here, of course, is set form is valid to false anytime and error is found. We'll do the same right here because, of course, the form isn't valid if this validation doesn't pass. Now we just need to pass our list of errors down to our child component. So I'll say errors equals this.state.errors. So we're passing down this error data to the child, but we also need to make sure that our child form is passing it to its children. So we have an on change here, but we aren't passing down any errors to these children. Each of our inputs is wired up to take an error string, if any exists. So in this case, I will say this props errors first name because we're looking for any errors that have occurred on first name. Then we're going to do the same thing right down here on this text input, but, of course, it will be looking at last name and any errors that might have occurred on last name. Okay, so now we have our errors passed down to our child components, and then all the way down to the text inputs. We'd even go look at the text inputs, and remember, one of the things that we already wired up in here was displaying the errors right here. So we should be all set if we go over to the browser to see our validation happen. If I just take an empty form now and hit Save, we can see the validation occur right here, although we have something wrong because it should have validated both of these. Also, it's saying last name must be less than three and it's putting it under first. Obviously, we made a typo somewhere over here in our validation. And by we, of course, I mean me. I'm not blaming you. There we go. This should say last name cause, of course, I am checking for an error on last name. So let's save that and go back to the browser and try again. Aha, there we go. So our validation is now working. Now if I enter a name of at least three characters and hit Save, then that message goes away. You can see the nice thing is since we created that centralized text input, we're getting the styling automatically, we have a consistent place where those error messages occur. So we've abstracted all that away, and we have a nice separation of concerns because over here, our Manage Author page just sends down error data. Then our author form just passed that error data down again to that child component. Then this child component, text input, finally contains the markup that determines where this error displays. So having the centralized in one place will keep our user interface consistent. So great, we now have validation working on our form. However, this author form has started making a lot of assumptions about the data it will receive, but it's not validating any of that. So in the next clip, let's add in PropTypes so that we can validate the data that's being sent into the author form component.
Our author form component has gotten fairly complicated, and it makes a lot of assumptions about the data that it's going to receive from the parent. If you look at this form, you can see that we're expecting first name, on change, a string of potential errors and a click function to be passed down. So why don't we add in PropTypes to make these expectations clear. I'm just going to paste in the PropTypes up here above and explain them. So we're saying that passing an author is required and that author should be an object. We're expecting a save function, it's required, and the change function so that we can attach that to each of these inputs down below. Then optionally, we're expecting an object that contains any errors that have occurred in this form, but we're not requiring it, since I didn't put is required here on the end. So this is nice because it effectively declares at the top of the file our expectations for anybody that consumes this component. This is useful if you have a component that's going to be used in a lot of places. Now I want to show you the other thing that it buys us, which is good error messaging within the browser. So let's go over to Manage Author page, and I'm going to take out on change. We'll just stop passing it down to the child component. Remember, we just declared that that's required. So we'll come back to the browser and open up the console. What we can see here is required prop on change was not specified in author form. Check the render method of Manage Author page. This is one of the things that I love about React. React does a really good job of providing you rich error messaging that makes it really easy to know what to go change when you've made a mistake. So now we can see, we just need to go back to our code, put in the on change event. We can go back to the browser. If I turn off preserve log, I can clear this out, and refresh, we can see we no longer get the message because we are passing down all the required properties. There's another tweak that we can make to improve the user experience on our form. Right now, if you start entering that form and you navigate away, you'll just lose that data. Let's put React Router to use again to help avoid people losing their input by navigating away on accident.
In the previous React Router module, we created a silly example of using willTransitionFrom to notify users that we're moving away from the About page. We said, "Are you sure you want to leave a page that's this exciting?" Well, this was obviously a silly example, but now we can implement a more real-world example. What I'd like to do is if I start entering data here and then navigate over to Home, I'd like to be notified so that I don't lose my half-filled out form. I don't want to lose my work. On a short form like this that doesn't seem that necessary, but you can imagine on longer forms how helpful that would be. So let's go over to the code and make this happen. To do so, I'm going to define statics. I like to put it up here, although it really doesn't matter where you place it. We will use the willTransitionFrom method. Just by convention, that's what it needs to be called for React Router to find it. This takes two parameters, transition and component. In here, we can do our check now. What I want to check for is whether the state is dirty. So I want to say if the component's state is dirty, in other words, has data been entered that hasn't been saved yet. Now, we haven't created this dirty Boolean as part of state yet, so let's go down here, and as part of get initial state, we'll just add another item, and dirty will be initialized to false, won't be true until the form is changed, so our check will be if the component state is dirty. Then we also will want to ask someone. This is where we'll prompt someone. The prompt will say, "Leave without saving?" So we're just confirming to the user that they are intending to leave without saving their data. If they don't answer that positively, then we will go ahead and abort the transition. So this is saying check whether the state is dirty, and then ask them, "Do you want to leave without saving?" If they say cancel, then go ahead and abort their transition. Don't move to that new link that they just clicked on. Of course, we need to keep track of whether the state of the form is dirty, so let's see how to do that. We're initializing it here, we're checking it here, but where do we change it? Well, that will occur in our change handler. Remember, on change we are calling set author state. So let's go to set author state. We'll call this .set state because remember, you always call set state to manipulate state. In this case, I will say that dirty is now true because we know that anytime that set author state is called, the form is now dirty because a change has happened on that form. There's one other place where we'll want to change the state for the dirty flag. That's down here after we save the author. Once the author's been saved, we know that the form is clean again. So right here we can set dirty back to false. Checking over here at the console, it looks like we have an issue. On line 22, it looks like we have some kind of typo. So let's just check up here on line 20. Yes, it looks like I just was missing a comma on statics. With that tweak in, we should be ready to try this out in the browser. Let's go over to Authors and hit Add Author. I'm going to start to enter some information and then click on a link. When I do, you can see that I get prompted, Leave without saving? If I hit OK, then I'm going to lose my information and navigate away. But if I come back in here, enter the same information, try to navigate away and hit Cancel, then now I stay on here, so I've saved the user from losing their information. Now we've created a nice, robust form that thinks about the user and makes sure that they have a pleasant experience interacting with our application to manage their data. We have one final issue to resolve. Right now, if I go over to Authors and I click on my name, you can see that it's not populating here, so even though we are passing in data in the URL, it's not being used to populate this form. So in this next clip, let's set up editing existing data so that we can save changes to existing authors as well.
Form Hydration via URL
We now have Add Author functionality working great, but you've likely noticed that one obvious thing that's missing is the ability to edit author data. If I click on my name, I'm taken to the 404 page. This is actually very close to working right now. There's three simple things that we need to do to get edit author working. There's three simple things we need to do. First thing we need to do is define a route that will handle this. As you can see, we've created a route to handle adding authors, but we haven't created one that will handle editing authors. So I'll paste in a new route for managing authors. As you can see, this route is a lot like the add author, but the big difference is right here on the path. What we're saying is go to author slash ID. This is a placeholder, and then this placeholder allows us to access this URL parameter by name. What will happen is React Router will inject this property down into our component. We'll see how to do that in a moment. Then, of course, we're referencing the exact same component because the Manage Author page component will be used for both editing authors and adding authors. The only real difference is just the path that we defined here. Now that we've set up our routes, the second thing we need to do is update the author list component because when we moved over to links in a previous module, this was one place that we didn't update. So we really should be using a link here instead of hard coding in a link, and this isn't even the exact link we'd want, so let's take this out. We'll put in a reference to the link component instead. So now we're calling the manage author route that we just defined, and we're passing it some parameters. As you can see, we just pass it an object. We're saying set the ID to author ID. Of course, to use the link component, we need to add a reference to React Router and to the link component up here at the top of the page. Okay, now we've created the route and we've used React Router's link component to link to that new route and pass in the parameter of author ID. The third and final step is to update the Manage Author page to populate the form when it sees the author's ID in the URL. So let's go over to the Manage Author page. To make this happen, we need to choose the right life cycle method. When you're hydrating a form, a good place to do that is componentWillMount. So let's define componentWillMount. This is just called, by convention, before the component has been mounted. There's a good reason to use componentWillMount rather than componentDidMount, in this case. That's because calling set state in this function will not cause a re-render. So we want to set the state before the rendering occurs. Here, let's get a reference to the author ID that's sitting up there in the URL. React Router makes this really easy because it passes the params down under props. What it does is it adds params under props, so ID just sits under there. I just say this.props.params.id. Effectively, this is from the path author ID. Here we can do our check because this won't always exist. Of course, if you're adding an author, then this segment wouldn't be in the URL at all. Here I will just set state and update the author object. What we'll do is call the author API and say get author by ID, and pass it the author ID. So in this way, if there is a parameter in the URL, we make a call via AJAX to get that author by ID and update the state accordingly. We do all this in componentWillMount because this way the rendering function won't fire twice. This is getting called before the method is rendered. Now, of course, if we were making an asynchronous call, which would often be the case, then your code would look a little different here. You'd have to handle that asynchrony via a callback or via a promise, but our mock API is just giving us a way to synchronously get this data. Of course, in the next module when we work with Flux, this will all get refactored as we interact with Flux actions and stores. But for simpler applications, there's nothing wrong with just making an AJAX call here from our controller view.
Let's wrap up by considering what we learned by building our first forms in React. We saw that any input that has a value is a controlled component, and if it's a controlled component, you need to create a change handler, or the user's input won't register. Most of the time, you'll want to work with controlled components, but any components that don't set a value are uncontrolled, and thus, operate like the normal inputs that you've been used to all these years. We created low-level reusable components that abstract away native HTML inputs. This helps us enforce consistency in the UI and also avoids having to deal with the extra markup required by Bootstrap in multiple places. We saved author data by making calls to the author API in the controller view. We programatically redirected to the Author page after saving an author using React Router's transition function. We simply passed it the named route for the author list page. We downloaded Toastr via MPM to display notifications after saving and we added validation to our form to assure we get valid input. We used PropTypes in the author form function to assure all callers are clearly aware of the props that must be set. We used React Router's willTransitionFrom feature to alert the user if they try navigating away from a dirty form and potentially losing their work. Finally, we pull the author ID parameter from the URL to populate the form. We saw how React Router passed data down to the component via props. This allowed us to easily support editing existing author as well. We wired this logic up in in componentWillMount life cycle function because it runs before rendering. So now we have a robust process for building forms. However, as an application grows larger, making data calls from your React controllers can start to create maintainability issues. That's where the final capstone module of this course comes in. In the next and final module, we'll explore how to implement unidirectional data flows with Flux.
Three Core Flux Concepts
Now you're likely familiar with the MVC pattern. The MVC pattern uses models, views, and controllers which interact with one another. React combined with Flux is a very different approach, so try to ignore anything that you know about MVC as we discuss Flux today. Flux is a totally different way of thinking about data flows and it seems complicated at first, but to understand Flux you really just need to learn these three new concepts that we saw in the previous slide: actions, dispatchers, and stores. Actions describe user interactions that occur in your React components. So for example, the user may click a button. These actions are handled by centralized dispatcher. The dispatcher is a singleton registry, but that sounds more complicated than it is. It's really just a centralized list of callbacks. So the dispatcher makes calls to stores. Stores hold your application's data, and not surprisingly when the data of the stores is updated, your React view reflects the new data. This is a unidirectional flow. In other words, the UI never updates the data directly and the data never updates the UI directly. Updates to the UI are ultimately rendered because of changes to the store. So let's look at some charts and see if that helps. In the next few clips, we'll help remove the mystery of actions, the centralized dispatcher, and stores, by discussing them in more detail.
Actions encapsulate specific events that occur within the application. An action might be save user, delete item, etc. The dispatcher exposes a method that allows us to trigger a dispatch to the stores and to include a payload of data, which is called an action. Action creators are dispatcher helper methods and they describe all the actions that are possible in the application. Now actions are typically triggered in two different places. First, when a user interacts with user interface, the view will call the appropriate action. Typically action creator functions are grouped together in files that have related actions. So for example, a Pluralsight course actions file would contain a list of action creators related to Pluralsight courses like create course, rate course, etc. Action creator methods add a type that is stored in a constants file. This type is used by the dispatcher to properly handle the action and pass updates to related stores. The second place that actions are triggered is from the server, such as a page load, or when errors occur during calls to the server. Now action payloads have a common structure of a type and data, so let's take a look at this example. This is an example of an action that occurs when a user is saved. As you can see, it has a type of USER_SAVED and it returns data consisting of the user's first name and last name. So an action sounds complicated, but it's really just this simple structure. Of course the data will vary depending on the action, but every action payload has a type and data.
All data flows through the dispatcher as a central hub. The dispatcher is a singleton, so there's only one dispatcher per app. The dispatcher, not surprisingly, dispatches things. What does it dispatch? Actions. Stores register with the dispatcher so that they can be notified when data changes. So the dispatcher simply holds a list of callbacks. The dispatcher invokes the callbacks that have been registered with it and it broadcasts the payload that it receives from the action. This effectively dispatches the actions to the relevant stores. This centralized dispatcher design creates a single point where multiple stores can request updates when a given action happens. This makes the application's data flow predictable. Each action updates specific stores based on the callbacks that are registered with the dispatcher. So basically the dispatcher distributes actions to the stores. The dispatcher exists to help get messages from views to stores. As you'll see, your views will ultimately respond to a different kind of event. While we're discussing dispatchers, I should also mention constants. With Flux it's helpful to create a constants file. This file helps keep things organized by defining constants that are used throughout the system in a centralized place. As you'll see, it also serves as a handy high-level overview of all the things that our app actually does.
Stores are the place where our application's data is stored. Stores hold application state, logic, and data retrieval methods. However, a store isn't a model like you'd think about in traditional MVC. Instead, a store contains models. This will make more sense in a moment when we look at an actual example of a store. An application can have a single store, but as your app gets larger you'll likely find it useful to create multiple stores. This helps keep related data grouped together in well-named stores and since Flux has a unidirectional data flow using a centralized dispatcher, you can rest assured that the proper stores will be updated for each event that occurs in your user interface. Now stores get updated because they have callbacks that are registered with the dispatcher. Remember, the Flux dispatcher keeps track of all the stores that would like to be notified when a given action occurs in your application. But here's an important note. Only your stores are allowed to register dispatcher callbacks. Your React components should never try to register with the dispatcher directly. Finally, as you'll see, Flux stores are extended with Node's EventEmitter. This allows our stores to both listen to and broadcast events. It also allows our React components to update based on those events. Our React components listen to our stores. Stores emit changes using Node's EventEmitter and this is how our React components find out that our application's state has changed. So the React components that use a store's data are redrawn when the state changes. In summary, stores are just what they sound like. They're a centralized place where the application's state and logic is stored. Stores have no direct set or methods. instead, they only accept updates via callbacks that are registered with the dispatcher. So these callbacks say, "Hey dispatcher, "when something changes, let me know about it." And this is a key concept. The store's the only thing in your application that knows how to update data. This is the most important part of Flux. The event we dispatch doesn't know how to add or remove items. Only the store knows how. Now you'll notice that when we implement multiple stores, there's a common shape to the store structure. These functions are the core interface that you always implement. You extend EventEmitter so that your store can emit events to tell your React components the data has changed. And you expose methods for adding and removing change listeners. This lets you easily say, "Hey, I want this React component "to know when this store changes." And finally, you of course need a method that actually emits the change. Of course if you want to avoid this duplication, you can use your choice of design patterns to refactor it out. Now remember, there may be multiple stores in a complex application. For example, you may decide to store your user, address, and product data in separate stores. And this is a great example where unidirectional data flows in Flux really shine. In this case, the dispatcher will send the payload to all three of these stores so that they're aware of any actions that have occurred which might update the store's data. And remember the dispatcher always dispatches the same payload. The payload has type and data. The type tells the store what action just occurred so that it can respond accordingly. So for example, again here's the payload that we saw earlier and it has this common shape of a type and then data. As I mentioned earlier, the Flux pattern admittedly requires extra code. You have to register callbacks with the dispatcher, which is certainly a bit of extra work. But as your application grows, this explicit one-way data flow is really helpful. And this quote's straight from the Flux documentation. It says, "As the application grows, "the dispatcher becomes more vital, "as it can be used to manage dependencies between the stores "by invoking the registered callbacks in a specific order. "Stores can declaratively wait for other "stores to finish updating, "and then update themselves accordingly." So this quote is really a reference to the waitFor API that's part of Flux. And I'm not going to go through that in this course because we really just don't have a need for it in this simple example, but I want you to know about it because what the waitFor API allows you to do is specify the order that your stores are updated when a given action occurs. This can be really useful when there are dependencies between your stores.
We discussed the concept of controller views a bit in the React module, but I want to mention it again here because it has implications when working with Flux as well. Now React has the concept of a controller view, and this is really just the top-level component that often ends up composing child components. So why am I using the term controller view here instead of just calling it a React component? Well, controller views control the data flows down to all child components. So the name controller view actually makes a lot of sense. I mention controller views again here because they're the components that should interact with stores. When a store emits an update, the controller view should receive the update and thus pass the updated data down to its children. The child components are automatically updated via their props. Now remember, controller views hold data in state and send data to children as props. In React, you can nest components as deep as you like, but it's recommended to have a single top-level controller view that interacts with the store, holds the data in its state, and passes all the necessary data down to its children. It's helpful to just pass a single large object down to children via properties so that you don't have to change code in multiple places as new properties are added to data objects in the future. And although it's possible to nest controller views, it's not recommended. Doing so can cause multiple data updates as React's render method gets called multiple times. That hurts performance. So it's recommended to have a single top-level controller view per page, or at least a single controller view for each major portion of the page. This means all child components simply receive data from the parent via props. This is already how we wired up our components in the previous module that introduced React, but I wanted to mention it again here so that we were clear that the controller views are the views that should be interacting with our Flux stores.
Flux Flow in Detail
So now that we've walked through an example end to end, let's consider a more granular example to help really drive the point home on how Flux works. The flow begins with an action occurring. Imagine I just clicked the save user button in the user interface. This generates an action, and when the action occurs the action sends a payload to the dispatcher. The payload always has the same structure. It's an object with two properties: type and data. Here's an example of a payload that might be sent by the action if I just clicked save user. The dispatcher checks its list of registered callbacks to determine which stores should receive the payload. And the dispatcher sends the payload that it received to all the stores that have registered callbacks. The store updates its internal storage based on the payload that it has received. In the case of saving a user, the store will update the user's data. If a new user is created, the new user's key would now be reflected in the data set. Once the store is done processing the update, it emits a change event. And emitting this change event notifies React that data has changed. Now React knows that it needs to re-render the UI to reflect the new data. Finally, of course, any new activities in the UI may generate a new action and cause this unidirectional flow to start all over again. And although I didn't mention it earlier, actions commonly make AJAX calls to make web APIs and get and receive data as well. So that's it. Okay. Saying that's it makes it sound simple. I know it may not feel that way. Yes there's a lot of pieces which can be frustrating at first, but trust me, the clarity of being explicit and having all data flow in one direction makes even very complicated applications very predictable. Traditional two-way binding certainly saves typing, but it can become difficult to understand what the application is doing if view models fire off cascading updates to other views and view models. This unidirectional flow requires more typing, yes, but once you understand these concepts I find it creates code that's easy to navigate and easy to reason about because it's very explicit.
A Quick Chat with Flux
Okay so one more way to think about Flux. We just went over the core players in a Flux application: the actions, the dispatcher, stores, and React views. I know it's a lot of new concepts and it can get confusing. It's likely different than anything that you've used before. So I find it helpful to think about these players as different people who interact with one another. So here's an example conversation that I play through in my head. React says, "Hey CourseAction, "someone clicked this "Save Course" button." And the action says, "Oh, thanks React! "I registered an action creator with the dispatcher, "so the dispatcher should take care of notifying all "of the stores that care." The dispatcher says, "Oh, well let me see who cares "about this course that was just saved. "Ah! Looks like the CourseStore has registered " a callback with me, so I'll let her know." The store says, "Ah, hi dispatcher! "Thank you for the update! "I'll update my data with the payload you just sent. "Then I'll emit an event "for the React components that care." And finally React goes, "Ooo! Shiny new data from the store! "I'm going to update my UI to reflect this!" So this is the chat that happens with Flux and with these four different players discussing a unidirectional data flow.
As I said, Flux is really just a design pattern. So once you understand the philosophy of this pattern, there's not much actual code to review in Facebook's Flux. The dispatcher's a singleton that registers callbacks, and these callbacks are registered when actions happen in your application. Thus the core API for Facebook's Flux contains only five functions, and all of these revolve around the simple idea of registering callbacks and calling them when actions occur. The first is register. The register function accepts a callback. This is how you tell the dispatcher that when this happens, you'd like to call a certain callback. unregister, not surprisingly, removes a registration from the dispatcher. So this is how you would tell a dispatcher to stop calling a callback when an action occurs. waitFor is a way to run callbacks in specific order. The idea here is if multiple callbacks are registered with Flux at the same time, then you may want them to be called in a certain order. This API allows you to specify the callbacks that should be completed before continuing execution of the current callback. The registered callback that you'd like to wait for is specified by passing a dispatch token of the callback that you'd like to wait for to the waitFor function. dispatch actually dispatches the payload to all registered callbacks. This is how an action tells the dispatcher that something happened. So basically this is how the action says, "Hey dispatcher, go tell the stores "about this action that just happened." As we've seen, the payload is an object with two properties: an action type and the data itself. Finally, isDispatching is a boolean that is true when the dispatcher is busy dispatching things. One final note. A lot of people get confused about whether Flux is really just a Pub/Sub model. If you're familiar with the Publish-Subscribe pattern, this probably feels very similar. And they are certainly similar, but they differ in two key ways. First, callbacks are not subscribed to particular events. Every payload is dispatched to every registered callback. And second, callbacks can be deferred in whole or in part until other callbacks have been executed. This is useful when you need to assure that one data store has been updated before another has been updated. This is what the waitFor API is for, that I mentioned on the previous slide.
So in summary, Flux is a pattern for unidirectional data flows. It's composed of three core concepts: actions, a dispatcher, and stores. Actions encapsulate events such as a user clicking on a button. The dispatcher is the centralized hub. It's a singleton, so there's only one per app. The dispatcher holds references to callbacks so that it knows what stores to notify as actions occur. Stores hold application state. You can have one if your app is simple, or multiple stores if you find that it helps. And of course once the store is updated, any React controller views that care are notified when it emits an event. And as we saw, Flux has many alternative implementations that are similar to what we just discussed. But the great news is, now that you know these foundational principals, it should be easy to check out and learn the alternatives. Now we've covered all the foundational concepts, so it's time to jump into code and put all of this to work. So the next module is all code. It's time to update our application to utilize Flux for unidirectional data flows.
Welcome to the final capstone module for the course. In the previous module, we learned about the Flux pattern. We discussed the core concepts in Flux, including actions, dispatchers and stores, but that's enough concept because now it's time for demos. Let's get back into the code and put these ideas to work.
To get started with Flux, let's create a folder for the dispatcher. There's only one dispatcher per application, so it might seem odd to put it in a folder, but I like having a folder for each major component of Flux. So I'll put it here under the source folder, and we'll call the folder dispatcher, and within the dispatcher folder we'll create a new file and I will call it appDispatcher.js. Now, watch close, because I'm about to paste in a whopping two lines of code. Boom, pretty exciting, huh? That's it; this is our dispatcher. We'll simply use Facebook's dispatcher. It's a singleton so, in other words, there's only one dispatcher per application. As you can see, we're simply returning an instance to the Flux dispatcher. The dispatcher is the central hub for the application, but it's not complicated. It really just holds a list of callbacks. All of the actions in our application will be dispatched via this dispatcher, and the stores will end up registering with this dispatcher to say that they'd like to be informed when actions occur in our system. And, as you can see, I'm making no changes to Facebook's Flux dispatcher. I'm just referencing it and instantiating it. Now that we have the dispatcher covered, we can look into actions.
Now that we've covered the dispatcher, we can move our attention to actions. To get started with actions, let's create an actions folder under source. I find it helpful to create a file for related actions, so this file is going to contain actions that are related to authors, so I'll create a new file and save this as authorActions.js. I'll start this as usual with our "use strict" statement, and the first thing we will want is a reference to the dispatcher that we just created because we're going to call this dispatcher in a moment. And next, we can define our author actions right here, and of course this module will end up exporting author actions. So now we have our shell and we can begin adding our actions here on line six. For our first action creator, let's call it createAuthor, and it will be a function that takes an author. Now, think about this: what is the first step in creating an author? Well, that is calling our API, so let me add a reference to the API up here, and now we can use the author API right here. Let's hold a reference to a new author, and we'll call it the AuthorApi and say saveAuthor, and of course pass the author that we've received. So, at this point, we've called our mock API and we've gotten back the new author. Now we're ready to dispatch this action. To do that, we call Dispatcher.dispatch. So this entire function is our action creator, and what we've placed right in here is the actual action. And an action has two core properties. First, it has an action type, and second it has data, and that data can have whatever shape you like. I could have multiple pieces of data that I pass here, but for this particular action we're just going to pass author data. So I need to define an action type here. Now, one way that I could do this would just be to put in a magic string, but the problem with using a magic string is I'd have to remember to type it the exact same way here in my actions and over in the stores that we're going to implement shortly. Those two have to stay in sync. So, instead, what we should really do is create a constants file and that constants file will contain a list of all the action types that we utilize in our system. So I'm going to create a constants file, and within constants I'll create a new file and call it actionTypes. This file is simply going to have a list of the action types that we used in our system. So I will just module.exports directly right here, and then define our list. Right now the only one will be CREATE_AUTHOR, and I'll just type this on both sides at the moment. So you can think of this a bit like an enumeration in C#. This is just a more strongly-typed way for us to have a list of action types that we used throughout our system. However, rather than having to type both the key and the value here, we can use a library that comes with React called keyMirror. I'm going to paste in a reference to keyMirror right here. And what keyMirror does is it takes whatever key I define over here and it copies it here over to here. So, this way, as we add more action types to this list, we only have to define the key over here and it will get copied over to the value side automatically by keyMirror. Of course, for this to actually happen, I need to pass all of this to keyMirror. And that's it, this is our action types file. As we add other actions to our application, we'll just put a comma here and put another action in. Now, the nice thing about this file is anyone that's new to our application can go to our action types folder and we'll get to see a list of all the different action types that happen in our application. As we go through the rest of this app, we'll be not just creating authors but also updating existing authors and potentially deleting authors, creating courses so you can have a nice, single place where you can see all the actions that could occur in your application. Now that we're done with action types, we can put them to use in our author actions file. So I'll add a reference to the action types that we just created, and now we can put them to use right here in our action and I can say ActionTypes.CREATE_AUTHOR. So this is our action type, and the other piece here is the author itself. Of course, in this case we'll just be passing on the new author. And now what we have is our first action creator. To clarify, the action creator is all of this, and the action itself is just this little payload here. And action always has a type and then some data, and that data can have whatever shape you like. So you can think about our action creator here as just this handy helper method that ends up wrapping our actions. It makes it easy to create these actions and have them dispatched through the dispatcher. I know that's a lot to take in, so let me add in a comment that really summarizes what's going on right here. Basically, this is saying, "Hey dispatcher, "go tell all the stores that "an author was just created." Simple as that. Now, of course we haven't created our stores yet, but effectively this call says, "Hey dispatcher, "go tell all the stores that an author was just created," and then any stores that care about this action will be notified and they can update themselves accordingly using this payload. Before we close out actions, I just realized a couple of minor typos. You need a comma here, and also need a semicolon right here. Just one final note, remember, our mock API is synchronous, but typically you'll be making ajax calls in a real app, so you'll probably be handling some sort of a callback or using promises right here to be able to handle the response from your web API. But we now have our first action creator set up, and that means that we can move on to exploring stores. Stores will take the data that we are dispatching and put it to use.
Stores: Change Listeners
We've set up our first action, we created an action types constants file, and we implemented our centralized dispatcher using Facebook's flux dispatcher. Now it's time to create our store; as we covered in the slides, you can have one store per application, or you can create multiple stores as your application grows. I find it helpful to create a store for each major concept or domain in the application. For now, our application solely works with author data, but I expect this app to handle courses in the future, so for the first store I'm going to give it a specific name. Let's create an author store. To do that, I'm going to create a folder first where we will put our stores, and I'll add a new file and call it authorStore.js. To get started, our store is going to need a reference to the dispatcher that we created earlier and we'll also need a reference to the action types that we defined in the previous clip. Every store will also use nodes EventEmitter. Nodes EventEmitter is used to broadcast events from our stores so that React components are notified when stores change. So, to support this important role of stores, we begin by adding a reference to nodes EventEmitter. You'll see how we put this to use in a moment. We're also going to need a way to extend our store to become an event emitter, so we're going to need to pull in a library from NPM to make that happen called object-assign. As you can see, object-assign is basically a way to glue two objects together. Calling object-assign on these two objects gives you a single object with both of their properties. We'll use object-assign to glue together our author store and the EventEmitter prototype. That sounds kind of confusing but, really, this is just an easy way for us to add EventEmitter capabilities to the author store that we're about to create. So the object-assign package on NPM is a ponyfill that gives us this same functionality in ES5 and, yes, ponyfill is a bit of a strange term but really a ponyfill is just a polyfill that doesn't overwrite the native method. So since object-assign is native in ES6, this is just a ponyfill that makes it work. This is just a ponyfill because it makes that same native functionality work in ES5 as well. Now, let's jump over to the command line and install object-assign. And I'm going to hit Control-C to stop my gulp process and we will say NPM install save object-assign. Great, and now that that's installed we can put it to use. I'll add a reference to object-assign, which we just installed and now we have the core libraries that we need to be able to create a new store. I'm going to call the store AuthorStore and then the first thing I will do is use object-assign and I'll pass it a few parameters. The first parameter is the existing base objects, so I'm just saying take an empty object and then extend that object using EventEmitter.prototype and now we can pass it another parameter which ends up further extending this object. That's a lot to take in, but let me explain this again. We're using object-assign to go in and basically say take an empty new object, extend that object to utilize EventEmitter.prototype and then finally we're going to define the rest of our store right here, so whatever we put in here will get mixed in. So whatever we put in here will have EventEmitter.prototype functionality as well. To some degree, you could think about what I've done here as defining EventEmitter.prototype as our base class. Now we're ready to define the store itself. Every store needs to provide a way for our React components to interact with the store. To do that, you want to supply three core functions: addChangeListener, removeChangeListener, and emitChange, so let's define those three. I'll start with addChangeListener. This function accepts a callback. So this function will be useful for our React components to say, "Hey, "I would like to know when this store changes." And inside we will say this.on(change', call the callback. So it has a really simple implementation here, just a one line implementation. Whatever callback gets passed in is going to get called anytime that things change in the store. Now we can define the second of the three core functions, which is removeChangeListener. This is also a function that accepts a callback, so this is really just the mirror image of addChangeListener. So instead I will say, this.removeListener. Again on change, and then define the callback. So now we've defined a way to addChangeListeners and removeChangeListeners. These are the ways that React components can tell stores that they'd like to be notified as they change. Of course, the third that I mentioned was emitChange, so let's define that function. And in here, we'll simply call this.emit(change'). Very, very simple. So this is all utilizing EventEmitter.prototype's interface and functionality. We're just defining some handy helper methods here because our react components care about the change event occurring within our stores. Now, you probably notice that I typed the same magic string three times, so let's create a constant and clean that up. I'll create a constant called change event and now I can reference that change event in all three of these functions instead of passing around the magic string. We've now defined the core of our store, but there's one piece missing. We still haven't registered with the dispatcher so that we're notified when actions occur. So, in the next clip, we'll set that up.
In the previous clip, we set up three core functions that are part of every author store: addChangeListener, removeChangeListener and emitChange, but there's another piece that's a part of every store. You have to register the store with the dispatcher so that it's notified when actions occur. This is typically defined below the store itself since it's a concern that's private to the store and thus not a part of the public API. To get started with that, I'm going to call dispatcher.register and I'll pass it a function that takes an action, then close this out. This function is going to get called every time any action is dispatched, and by that I mean any. This is a place where Flux's dispatcher implementation differs from traditional published subscribed patterns. Every store that registers with the dispatcher is notified of every single action. So here we're going to need to add some logic that switches based on the action type that it's being passed. Let's add our switch statement and we'll switch on actionType. Within this switch statement we'll have a case statement for each one of the action types that we defined in our application. Of course, the only action type that we defined so far is create author. Before I do that, let me go down here at the bottom and put in our usual module.exports and we will export the AuthorStore. Now, we're exporting AuthorStore because this is the public API for our store. This is a private implementation detail that just registers our store with the dispatcher. And before I move on and fill out this switch statement, I want to stop for a moment and discuss this structure. We now have all the boilerplate code set up for our store. Yes, I just said boilerplate; we still haven't added any code that's specific to our purpose yet. So what you're seeing here is the basic shell of any store that you create. I know what you're thinking, "Wow. "That's a lot of boilerplate," and you're right, it is. But remember this, if you create a large app with multiple stores, you can of course use various design patterns to eliminate the boilerplate that you see here, but I'm deliberately showing you the full store implementation here to assure that you're clear about what stores are and how to create them. And, as I mentioned before, there's a long list of alternative Flux implementations out there to choose from, but understanding the actions, dispatcher and stores that we're creating will help you easily pick up alternative implementations that have less boilerplate code and configuration, because all of the alternative Flux implementations build upon these same principles. And, of course, everything we've done here, we only have to do once. Adding more features to our store will be much quicker, as you'll see in a later clip.
Stores: Private Storage
Of course, our store isn't very useful yet since it's not storing anything. Let's fix that. Since this is the author store, it will store data. Let's create a private variable up here at the top. I'll prefix the name with an underscore to help visually clarify that it's private. It's enforced as private too because it's not exported out of this module, only the author store is, and that's a good thing because it means that no one can mess with the data in the store unless they interact via the public API, which is the functions that sit within the author store. Each time an author is created, the create author action will be dispatched. It will include the new author as part of the payload. So let's take that new author and push it into our private array of authors down here where we register with the dispatcher. So add a case statement and check the ActionTypes. In this case, we're looking for an action type of CREATE_AUTHOR, which is the only action type we've defined so far. And we'll take our private variable and push new data into it using action.author. Now, let me clarify where action.author came from. If we go over to authorActions, remember, this was the payload that it dispatched. It dispatched an actionType and an author, so we know that we can get author off of that payload, and that's what we're doing right here, action.author. So these are effectively glued together through the dispatcher. This action is dispatched via Dispatcher.dispatch and then it's caught over here on the other side via Dispatcher.register, so now we've stored that new author within our private author's variable. The other thing we need to do is emit a change, so we'll just call AuthorStore.emitChange. This is just using the emitChange function that we defined right up here above. It's important that any time that the store changes we call emitChange, and, of course, we just changed the store state right here on 28. By emitting a change, any React components that have registered with the store will be notified and then they'll know that they need to update the UI accordingly. So any stores that ever called addChangeListener will then be notified any time that I call emitChange. Okay, one final piece and we'll have our initial build of the author store completed. We need to expose the author's data, of course. Remember, right now it's private since we deliberately created a private variable up here at the top to keep people from messing with the data directly. Let's create a getAllAuthors function in the author store. I'll define it right here below emitChange. And of course it will just return our private author data. There's one other function that would likely be useful, and that's getAuthorById. In here, we'll use lodash and its find function to search through the list of authors and then return an author that has the ID passed. Of course, we need a reference to lodash so I'll add it in up here at the top. Now that we've finished creating our store, we can move back to the ManageAuthorPage and put this store to use.
We've now wired up a unidirectional data flow for saving an author using actions, a dispatcher, and our first store called AuthorStore. Now, let's update our ManageAuthorPage to put this to use. Our first step is of course to open the manageAuthorPage and we're no longer going to need a reference to the API so I'm going to replace that with a reference to the actions file and with a reference to our AuthorStore. And since we removed the AuthorApi reference, we're going to replace that with a reference to AuthorStore. Now, it just so happens that the public method that we're exposing on AuthorStore is exactly the same, so all I have to do is replace AuthorApi with AuthorStore. If we scroll down in the saveAuthor function, you can see that we're currently calling saveAuthor on AuthorApi. Now, we're no longer referencing the AuthorApi because we're going to interact now via actions. So we're going to make a call to AuthorActions and initiate the createAuthor action, and of course we will pass it the same parameter which is the author that's within the state. That's all the changes that we need to make to manage author state. Now, we're using actions and our AuthorStore to be able to interact using Flux rather than our approach before that was directly calling the API. So now, make sure that you're running gulp in your command line, so now we should be able to insert an author using this new flow of Flux and everything should work just the same as before. I'll just enter a random name and hit save and now we can see this new author is saved successfully. So great; it's still saving but this page needs update to utilize our store as well. Right now, it's calling the AuthorApi every time on load. So let's update this component to utilize our store also. We'll go back to the editor and open authorpage. And, first, I'm going to remove the reference to AuthorApi and instead replace it with a reference to our AuthorStore and our AuthorActions. Down here within componentDidMount, we were getting all authors to set initial state. Now, instead we can do this right up here within getinitialstate. So what I'm going to do is cut this, paste it up here, and of course we're no longer calling AuthorApi, we're calling the AuthorStore. And we can do this because we can rely upon the AuthorStore to give us either an empty array of authors or whichever authors are currently stored in the AuthorStore, so this means we no longer need the componentDidMount function, so I'll delete this, and that's all the changes that we need to make on the AuthorPage, pretty simple. Let's go back to the browser and make sure we can still add an author. Of course, the first thing you'll notice is our list of authors is now missing. We're going to address that in a moment. But for now let's just make sure we can still add an author. I'll just put in a fake name, hit enter. So we can still create authors just fine, except now we're getting those authors from the AuthorStore instead of making a call to the API. So now we have a unidirectional data flow for saving authors working with Flux. In the next clip, let's fix the initialization of our AuthorPage by using Flux actions to initialize our application.
In the previous clip, we noticed that the list of authors isn't displaying anymore. That's because we have the store calling the API to get the data on page load previously. But now that we've moved to Flux, the stores will no longer have this responsibility. Instead, we'll generate an initialized action in main.js and that will pass the AuthorStore, the initial author list. To get started with this, let's go back to the editor and create a new actions file. There's a number of different ways you could bootstrap the application, but I like keeping all my application bootstrap code in a dedicated actions file and I like to call that file initializeActions.js. And I'll add some references to the top here. We'll need a reference to the dispatcher, to our actionTypes file, and finally to the AuthorApi because we're going to make a call there to get some data. And I will say var InitializeActions and of course module.exports = initializeActions. So in here we will define our action creators and there's only going to be one for this for now because our bootstrapping for this application is very simple but by putting it all here it can get more complicated and it's handled in one spot. And I will call it initApp. In here, we will call Dispatcher.dispatch and actionType for this I'm going to say is initialize, so I'll say ActionTypes.INITIALIZE. Now, of course, we need to go over to that constants file, which we'll do in just a moment. We can pass whatever data that we want here. I'm going to call it initialData since that's exactly what we're doing, we're passing the initial data for the application. For our initialData, I'm going to say that authors come from the AuthorApi and the call to getAllAuthors. And usual caveats apply here, I'm making asynchronous call because this is a mock API but you'd probably be handling callbacks or promises here in a traditional bootstrapping setup that's calling an API over Ajax. And we'll get our semicolon down here. Now, let's jump over to the constants file that contains our actionTypes and we will add a new action type of INITIALIZE. I think I will put it up here on the top line. It just seems to make sense because it's initialize, but it doesn't really matter what order you put these in. Just put a null here, and remember, keyMirror will replace this with what's here on the left, so we'll save that. This is our new action that we want to call when the application is initially loaded. Of course, the whole application is bootstrapped through main.js. This is the entry point for our application. So this is where we want to add a reference to this new action that we just created. So I'll add the reference and then to initialize our application, it's a one liner. I call InitializeActions.initApp. As I mentioned, this is just one style for initializing the application. For instance, I could have taken this right here. Oh, which by the way, glad I came over here. This should of course say dispatch. It's a lot more likely to work with that "S" in there. But I could have taken what we have right here and just pasted it in right here, made a reference to the dispatcher and the other related resources that we needed, but I find it cleaner to have all of our InitializeActions sitting in a dedicated actions file. That way, as things grow and we have other actions which need to run on initialization, then this can be a simple abstraction over any of those activities. So we should be set to go, but as I look over here, I realized that in my console I have an unexpected identifier in actionTypes.js, so let's go check this out. Now, it looks like I just forgot my comma when I defined my second item in actionTypes. Let's see if everything's going well; looks like it is. Now let's go back to the IDE and continue because we're at the point where we have this InitializeActions defined and within main.js we're ultimately calling that. We're dispatching this InitializeAction. However, we haven't set up any stores yet to pay attention to this action, and there's one store that obviously is going to care about this, which is our AuthorStore. As we create other stores, we'll likely do the same thing as we're about to do here. So let's go down here and handle this new action that we just created. So we'll need a new case statement and the ActionType here is INITIALIZE. On INITIALIZE, what should we do? Well, the whole point of this was to initially populate the authors array, so let's set that by saying action.initialData.authors. And, again, you can see that this is referenced over here because we said the data that we were returning in our payload was initialData, and then under that was authors, so that's why I'm saying initialData.authors over here in our store. Those just match up exactly. And then of course we need to emit a change so that anyone listening that cares is made aware of this change. Again, nothing is wired up to listen to the changes that we're emitting yet, but we will be doing that in a future clip. So arguably we could leave this line out, but I'm putting emitChange at the bottom of each one of these case statements because typically you're going to want to do so that your react components are made aware of the changes that just happened within your store. And of course we want break statements here. Looks like I was neglecting those when we had a single case, but obviously we want that. We also want, as usual, a default, but in this case that default is just a no op. There's nothing to do here because, remember, the dispatcher dispatches all actions to any stores that have registered with the dispatcher. So this store could potentially end up getting actions that really don't apply to it. As we get more and more stores in the system, we used these checks for ActionTypes to determine whether it's an action type that applies to this store, so that's why down here we just have a no op. In other words, there's nothing to do here. We now have the initialization set up completely wired, so we should be able to go over to the browser and see that the authors list is again populating, and indeed it is. The beauty of this new approach is as our application grows we now have a centralized point for initializing our app. If you have an app with many stores, this pattern is really useful and it makes it easier to understand how the application gets started. We now have Add Author working again, but one thing you might not have noticed yet is we've broken Update Author, so in the next clip let's see how to get that working with Flux.
Update Author Flow with Flux
In the last few clips, we've gotten Flux working to initialize the app and to create new authors. However, in the process, we broke editing authors. To get started with this, let's go to the constants file and add another action. The action type here will be UPDATE_AUTHOR. Now, we can go over to authorActions and create a new action. I'm just going to copy our createAuthor action because our updateAuthor action is going to be quite similar. Rename this to update, and then I'll rename this variable to updatedAuthor and, of course, down here we'll be passing an updatedAuthor and our action type here will be UPDATE_AUTHOR. Now, looking at this, you're probably seeing, "Oh, there's a fair amount of copy and paste here," and I agree with you. What I could do instead, of course, is just have a save author action, because both of these, generally speaking, are just about saving an author. I could have gone that route. It's really a judgment call. My thought is this: as your application grows, you may want to handle creating authors a little bit differently than updating authors. If you add a number of different points in your system that cared about authors, there may be some that care about the differentiation between creating an author and updating author. Nonetheless, I'm going to keep them separate for now, just to give us some extra practice on creating actions. I'll go ahead and remove this comment as well. And now let's go over to the manageAuthorPage and in here you can see that under save author, we're always calling createAuthor. Now we need to add a fork because if we're saving an author that has an ID, we know that we're updating an author rather than creating them, so let's add that code. If this.state.author.id, then we know we're updating, so we'll say AuthorActions.updateAuthor and pass this.state.author. Otherwise, we'll do what we were doing before and we'll call createAuthor. So let's save our changes here and then we'll jump over to the AuthorStore because the other piece that we haven't done yet is to wire up a handler for this new action type. I'm just going to copy our case statement for the createAuthor action type, paste it in. Now of course this is going to be called UPDATE_AUTHOR instead, and we're not going to push in this case. Instead, what we're going to do is get a reference to the existing author and then replace that author in the array, so this is really the same pattern that we used within our mock API. It's a lot of code but effectively we're getting the existing author by ID, then we're getting their index and then finally we're using the native splice function to find that existing author's index and then replace that index with the author that we received within our payload. And then, of course, when we're done, we emit the change. But nonetheless, all this code right here is effectively replacing the existing author with the new author, so you don't really need to understand it. It's just some boilerplate that's not specific to Flux. And, by the way, this is another place where judgment calls come in. You might find it preferable to take this code and refactor it out to a well-named method. This has probably reached the point where that would be helpful. I'm just going to leave it here for now so that we have consistency in our style, but it's certainly something to consider. And now that we have this done, let's save and check it out in the browser. We should find that we can come in, edit an author, and now my name got really long. Nice. Nonetheless, editing an author does work. Now, there's a little caveat to this. Editing an author would actually work without me even coming in to here and setting this up. And that's because we're actually getting a reference to the data in the store within the ManageAuthorPage. Right here, when I call the AuthorStore and I get it by ID, I'm setting the statement, I'm getting a reference to that variable, so editing that variable in the UI will effectively change the data within the store. Now, this may or may not be the behavior that you prefer, but it's what you're going to get by default. Nonetheless, what I did here to handle updating an author is useful because you can imagine that we could save data to the database and then get data back that is slightly different than what we initially saved here. So this would take whatever data we'd received from our API call and put it back into the store. It just so happens that here I'm pushing that exact same data back in, so adding this in wasn't absolutely necessary to make this work, but it likely would be necessary in a real application where calling up to the server causes some kind of a change to our data, maybe a timestamp change or some other related metadata. So now we have save and add working properly using Flux, but one thing we haven't done yet is wired up some change handlers on our stores from a React component, so let's make that happen. And as our use case for this will support deleting authors and will delete authors on the same page, and that's exactly a scenario where we'll want to listen to the store as it changes so that our React component immediately reflects that change without a page reload.
Adding Store Listeners
Great, right now everything is working fine. When we edit authors, we see the updated data from the store. Why? Because we redirect upon save. See, when the author page component is mounted, it gets the new state from the store in the get initial state function. But what if instead we wanted to stay on the same page and merely show updated data? Well, in that case, we need to listen to store changes. To show how this works, let's add functionality to delete an author from the author page. So let's go to the editor and open up the author page. First, we need to define a function that we want to run any time the store data changes. Let's create a function called _onChange. Within this function, we want to set state every time that the store changes, so we will add this.setState and the state that we're changing is author data. Of course, any time the store changes what we will do is call the AuthorStore and getAllAuthors. So this is the function that will run any time the store state changes. And remember that when we defined our stores we also set up ways to add listeners and remove listeners. We're going to consume those calls now from the author page component so that we can add a listener when this component is mounted and remove a listener when a component is unmounted. So I'm just going to paste in these two functions. They're very simple. As you can see, we call the AuthorStore and we add a change listener and we say any time that something changes, call our _onChange function. We just pass it a callback. And it's the same story with component we'll unmount. We remove the change listener, and when we do so this is the callback that we're trying to remove. So when our component mounts, we start listening; when our component unmounts, we stop listening, and this is the function that gets called any time the store changes. While we're here, I want to clarify, you might be wondering why we didn't also do this on the ManageAuthorPage. Well, I didn't because it wasn't necessary. On that page, when the author saved, the page transitions to the author page. So the resulting update to the store wouldn't be visible anyway. So adding these listeners there didn't add any value. I knew that we'd ultimately see the changes reflected when we redirected to the author page since the author page calls the store to get fresh state from the getInitialState function. We've now wired up our ChangeListeners, but in the next clip we'll set up deleting an author so that we can see how this work.
Delete Author via Flux
Summary: Stepping Through Flux
Congratulations. If you made it this far then you're fully prepared for my final challenge. You'd likely recall seeing the course management feature in the introduction clip for this course. But hey! We never built the course management section, um yeah we didn't. You're right. But don't worry. That was all part of my plan. Why? Because you already have all the knowledge you need to build it yourself. so my challenge to you is to put your new found skills to use and build the course management section on your own. There are admittedly going to be a few new challenges, like handeling interactions between the author store and the new course store that you will create. And you'll likely want to populate an author drop-down on the course management page as well So that will be another chance to create a reusable component. As you can see the format created asked for some basic data about the course. To get you started heres a link to a mock api for courses and here's a link to some mock course data as well. So, that should be all you need to get rolling on this new feature. I hope that you find working in React and Flux as fun as I do. Thanks a lot for watching.
Cory is an independent consultant with over 15 years of experience in software development. He is the principal consultant at reactjsconsulting.com and a Microsoft MVP.
Released12 Aug 2015