What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
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
Description
Transcript
Exercise files
Discussion
Learning Check
Recommended
Intro
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
These days, there's a wide variety of ways to create rich web application using Components. You can buy off-the-shelf Components from Telerik, like Kendo UI. You can use open source Components, like jQuery UI. Knockout offers Components. Ember offers Components. Angular has its own Component model that it calls Directives, and now there are even native web Components that follow a new emerging standard which I covered in a separate Pluralsight course. And given, some of these are pure Component libraries while others listed here are full-fledged application frameworks, but the point is, each offers a unique way to handle web Components. We have a vast list of options for creating Component-based UIs for the web today, and React is yet another option. So what makes React special? Why should you choose it over all these other options? Well, as you're going to see, there's some really great reasons to choose React. Let's consider the unique qualities that React has to offer. As you'll see in this course, React and Flux offer a number of interesting innovations, including JSX. JSX involves writing your markup in JavaScript. That sounds crazy, but give it time, I think you'll like it. The Virtual DOM, which enhances performance and efficiency by minimizing expensive updates to the DOM. Isomorphic Rendering, which allows you to render your React Components on both the client and the server, and Unidirectional Data Flows, which make your application easier to reason about by handling all your data flows in a single direction using a centralized dispatcher. Now to clarify, I'm not claiming that React and Flux are the first libraries to utilize these patterns and technologies, however, React and Flux are the first libraries to reach critical mass and help bring some of these technologies and approaches to the forefront. However, let me warn you, I like to joke that Facebook called it React for a reason. See, when React was launched, it created some strong reactions in people. This tweet was in response to the initial announcement. Many people didn't initially take React seriously because it was so different than anything that came before. React and Flux deliberately ignore some commonly held best practices. So if you're new to React and Flux, I'm going to have to ask you to keep an open mind. Try to limit your initial urge to quit and run out the door. I believe common practices should be questioned from time to time. Our industry moves so fast that it would be silly for us to blindly continue to follow the same path. So if you're willing to listen with an open mind, I hope I can sell you on rethinking the way that you're doing front end development. Here's a few controversial ideas that we're going to explore in this course. First, React takes the stance that HTML should be a projection of application state, not a source of truth. Just think about that for a second. Back in the days of jQuery, we made this mistake for years. We stored data in the DOM, in data and JavaScript. At any given point, which was the system of record? Anytime the JavaScript changed, all the UI had to be redrawn. React's Virtual DOM changes how you think about your app. Your app is a function of props and state. You don't store anything in the DOM. It's simply an implementation detail. The DOM reflects the current state of the data automatically. Second, as you'll see, React takes the stance that JavaScript and HTML belong in the same file. For years, we talked about separation of concerns as a common best practice, but is separation of concerns always a good thing? JavaScript and HTML are completely intertwined, and there's no strongly typed interface to keep them in sync, so as you'll see, there's some clear benefits to placing them in the same file. As we move on to Flux, we'll see that there are some advantages to unidirectional data flow as opposed to two-way binding, that you're likely used to if you've worked in Angular, Ember or Knockout. Unidirectional flows make your application easier to reason about and will offer us a simple path toward updating our data stores as application changes. Now this sounds crazy but when using React, Inline styles are actually useful again and can increase our code maintainability by avoiding the creation of bloated stylesheets, which are very risky to change. React Components allow you to utilize the power of JavaScript to write dynamic, deterministic, and Component-specific styles that are potentially easier to maintain than traditional stylesheets. And brace yourself, because we've entered a new era. In React, All Variables Should be Global. Global Variables are awesome! OK, I'm kidding. This was totally a test to make sure you're paying attention. Hopefully, you didn't just rate quit and turn off the course. In actuality, React in Flux does an excellent job of encapsulating and managing state, but the other previous points that I just mentioned are indeed legitimate arguments, and I'm going to make those throughout this course. Finally, while these ideas may be controversial, many well-respected companies have already embraced React, and they're using it on large-scale production systems. Not just Facebook and Instagram, but other names that you'd recognize like PayPal, Dropbox, Netflix, The New York Times and Yahoo. So React may be new to you, but it's actually a battle-proven library that's been used in production since 2013. Now that I've hopefully sold on the why, let's shift gears and talk tech.
Technology Overview
One of the hardest parts of creating a React application is making the decision on all the related libraries that you're going to use to build a full application. Remember, React is merely a view layer, so it doesn't have any opinions on how to handle things like data flows, routing, builds and deployments. Let's walk through the Core Technologies that we'll use to build our app. We'll use Node.js to run Server-side JavaScript. We'll use Browserify to expose these Node packages to the browser, and of course, we'll use React as our Components library. We'll use React Router to handle client-side Routing. We'll use Facebook's Flux to handle our application's data flows, and finally, we'll use Gulp as the Task runner that wires all of this together in an easy-to-use script. So, this is the technology stack that we'll explore in detail throughout this course. Let's discuss each of these technologies a bit more.
Node.JS Overview
Building an app with React and Flux doesn't require using Node, but Node certainly makes our job easier. Node runs JavaScript on the server using Google's fast V8 Engine. Node now includes npm, which is Node's package manager. npm includes thousands of useful JavaScript libraries, so we'll use npm to pull down the libraries we need for our project. If you haven't used Node, don't worry, we'll walk through the basics of what you need to know to get going in this course. As I said in the previous slide, Node comes bundled with its own Package Manager, called npm. npm is how we'll install and manage the dependencies we need to run our app. You're likely already familiar with the concept of the Package Manager, but in case you're not, let me mention a few others. In .NET, developers use NuGet, Ruby has RubyGems, Java has Maven, Python has pip, PHP has PEAR, and front end developers often use Bower to manage packages related to HTML, JavaScript and CSS. npm is Node's Package Manager. It provides a quick, easy way to install Node packages, just like these Package Managers you see above. Once you've installed Node, you simply type npm and the name of the package that you'd like to install. Now, Node use the CommonJS Pattern for referencing and exporting modules. You might be familiar with alternative module patterns like RequireJS' Asynchronous Module Definition pattern, also known as AMD. Node's CommonJS Pattern serves the same general purpose. Its goal is to encapsulate your JavaScript into a reusable module that can be referenced by other modules. It's simply a different syntax. In short, using the CommonJS Pattern consists of three core parts. First, you reference any modules that you want to use using a require function. You pass the path of the module that you'd like to import. If the module's in your Node modules folder you don't need to specify a path since Node will check there automatically. So if you've worked in Java, then this is like a Java import statement. If you've worked in C#, this is a lot like a using statement. Second, you Declare the module itself. You write all your code within this module, so it sits between these brackets, and you give it a name. And finally, at the bottom of your file, you export the module that you just defined. This Exposes your module so that other modules can reference this module using the require statement. We will build all of our JavaScript files using the CommonJS Pattern.
Browserify Overview
There are a variety of patterns and tools for managing and bundling dependencies in JavaScript. RequireJS is a popular choice, but Browserify has become increasingly popular because it bundles up Node modules so that it can use them in the browser, and that's really handy because npm has thousands of useful JavaScript libraries. So by simply using Browserify, all of npm's packages can now be easily used in the browser. Now, by default, Browserify bundles all your dependencies into a single file, so this is handy for minimizing the number of HTTP requests that are required to load your application.
React Overview
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 Overview
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.
Gulp Overview
To tie this all together, we'll use Gulp. Gulp is a popular JavaScript-based task runner. Grunt is a popular competitor that would work just fine for our purposes as well, but Gulp has recently gained a great deal of popularity because it's easy to configure and very fast. Gulp has a rich plugin ecosystem that allows us to easily automate all sorts of important tasks like minification, linting, bundling, moving files, running tests and watching files so your browser can automatically reload when you save your changes. Gulp is fast because it's stream-based. Gulp allows you to glue a number of tasks together by taking the output from one plugin and using it as the input for the next plugin. It keeps all this work in memory rather than writing to disk. This is what makes Gulp so much faster than its current primary competitor, Grunt. Gulp can perform hundreds of different tasks. Now, our primary use when building our example app is to watch files and run the Watchify plugin. This plugin will automatically bundle our code using Browserify as changes are made to the solution. Finally, to clarify, you don't have to use Gulp, Browserify, or Node to build applications with React and Flux, but as you're going to see, these tools really help streamline the process. It makes doing development in React and Flux really luxurious.
Versions
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 testing? The short answer is, I'm not discussing automated testing in this course. Facebook created their own testing framework called Jest, which they use internally to test React components. Jest is just a wrapper over the Jasmine testing framework. It offers a really low-friction way to get started testing React applications. I plan to utilize Jest in this course, but ultimately, I removed it from the course because at the time of this recording, there were issues installing and running Jest on Windows. Hopefully, these are resolved by the time you view this because I found Jest to be a really easy way to test React components when I'm working on my Mac. That said, I'd recommend looking into Jest. It's a really quick way to get started testing your React components, especially if you're already familiar with testing in Jasmine. You don't have to use Jest to test React. You can use any of the popular JavaScript testing frameworks like Jasmine, Mocha, or QUnit.
What About ES2015?
I was torn on whether to use the brand new version of JavaScript, ECMAScript 2015, that's more commonly referred to as ES6 in this course. ES2015 was officially ratified in June 2015. In contrast, the JavaScript that we've been using for years has been around in its current version, ES5, since 2009. So ES5 is the JavaScript you've been writing for quite a while now, and that's why I decided to stick with ES5 for this course. I don't want to alienate people that are still getting up to speed on the latest version of JavaScript. Now as you review other examples of React and Flux online, you may see some that are in ES2015 and others that are in ES5. To be clear, ES2015 is certainly the future, but to avoid confusion, I'm going to use ES5 throughout this course, the version of JavaScript that you've been familiar with for years. That said, I strongly recommend checking out ES6 and transpiling it via Babel. It's a great way for you to enjoy the newest version of JavaScript today, but for this course, we'll keep things simple and use ES5.
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.
Environment Setup
Introduction
These days there's a staggering variety of free tools available that enable us to do rapid client-side development. In the previous module we discussed many of these tools and in this module we'll install and configure our environment so that we receive rapid feedback as we build our application. Before I move forward and start walking through the configuration for our dev environment, I want to clarify that all of this technology is not required for building applications in React and Flux. In fact, most of what we're going to set up here isn't even specific to React and Flux at all. What this module is about is creating a dev environment that gives you rapid feedback as you build your application. So these tools are useful for anyone building apps in JavaScript. With that warning out of the way, let's move forward. By the end of this module we'll have a really slick development experience. All you will have to do is type "gulp," four letters, and all of this magic will happen. You'll compile your React JSX, your JSX will be linted and your JavaScript will also be linted, JavaScript and CSS will be bundled and migrated to the dist folder for your application, a web server will be started up automatically, and your browser will be open to that dev server URL. Finally, any time that you hit Save, your browser will reload, and you'll also get feedback on any typos you've made in your JSX or in your JavaScript, or any linting issues that are found via ESLint. Trust me, once you've done development with all this modern tooling in place you'll never want to go back. So this module walks you through all the initial steps to install and configure the development environment, but you may not need to watch this module. If you're already familiar with Node, npm, Browserify, Gulp, and ESLint, then you can safely skip this module. Just go download the final result off of my GitHub account at the link above. But I do recommend watching this module if you're not already familiar with these technologies. It's a great way to learn a bit about each in a short period. This module takes around 30 minutes to walk you through installing and configuring a powerful and modern JavaScript development environment. If you're excited about understanding how Node, Browserify, Gulp, ESLint, and a few other technologies can be utilized to make us more productive when we're building any JavaScript-based apps, including React, then this module is for you. So let's get started.
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
So far we've only written configuration to handle html files but we know we're going to have JavaScripts since we're writing a React and Flux app. Let's create just a simple JavaScript file to get us started and I'm going to call this main.js. This will be the file that bootstraps our application but to keep things simple, just to start us off I'm going to create a very simple test, and I'll just say "Hello world from Browserify." So really we're going to use this file to test that Browserify is working. Now Browserify uses the CommonJS pattern, and the CommonJS pattern uses module.exports at the bottom to define what the file exports. So Browserify will go through all of the files that we utilize and then wrap each one of these up into their own context, their own modules, because it is a bundler. So this will not be global code. It will be in its own module. And this module is exporting what I've defined right here, an app, which is just a very simple console log. So to get Browserify set up we need to go do some installations. Let's go over to the command line and we're going to install a few different packages. We're going to install Browserify, Reactify, which will compile our JSX, JSX is a part of React, which we'll discuss in the next module, but we're just getting this set up now, and then we'll also need vinyl-source-stream, which I'll explain in a moment. Great, now that these are installed we can go over to our gulp file. We just installed three packages so I will put in these require statements. We installed browserify which will bundle our JavaScript and Reactify which will transform our React JSX over to plain JavaScript and then finally, vinyl-source-stream which is going to allow us to use conventional text streams with gulp as we'll see down below. Now I've already defined a path for HTML, we'll also need a path for our JavaScript, and it's going to look very similar to our HTML path except we're also going to look in subdirectories for any JavaScript that we can find. And now that we have our paths we can come down here to the bottom and create our JS task. And for this task we're obviously using Browserify and we're going to pass it the path that we just defined up above. And then we're going to use Browserify's transform because what we need to do is transform any JavaScript that we get using one of the plugins that we just installed which is Reactify. And as we saw above, Reactify will be useful for compiling our JSX, which we will explore in the next module. And what we'll want to do is bundle everything that we get, in other words, put it all into one file, so it doesn't matter if we have 25 different JS files, they'll get bundled into one to save HTTP request. Now as it's doing so it is helpful to get error messages so I like to set this up as well and say console.error.bind to the console. So what this is saying is if any errors happen we're going to see those spit out right on the console. Next step we need to define where our bundle, what our bundle will be named, and I'm going to call it bundle.js and then we also need to define the destination for this bundle that we just defined. And for our destination we will go to config.paths.dist which we've already defined above, and then put it under scripts. One final piece is any time that this task runs we want to reload our browser. So as we change any JavaScript, we want to know that we're seeing the latest version of that JavaScript in the browser. And then one final piece we need down here is to put js in the list of default tasks, because we want js taken care of. And we also want to create a watch for our JavaScript task so that as our JavaScript is changed our JavaScript task is run automatically. So we've written our tasks that should create our bundle and sit it under the dist folder under scripts, and that bundle's going to be called bundle.js. So now we can reference that bundle over here in our HTML file, because assuming everything works as expected, it's going to end up in scripts and be called bundle.js. So we'll save our index.html. And I just realized I have not defined main.js so we'll need to come back up here to find that variable, so we'll call it mainJS, and it sits under source, and we will call it main.js. So this is just a variable that references mainJS file right there. And that will fill in our configuration for Browserify. So if we have this all set up correctly we should be able to go back over to the command line, type gulp, and this will open up a new browser window, and if I look at the source here we can see a reference to the bundle, and if I click on the bundle now we can see the code that Browserify generated. Now that code is minified so this isn't very readable but we can see our message did get placed into here. And that means if I hit F12 and open Chrome's developer tools we can see that it wrote to the console as expected. So Browserify's "hello world" is working properly. So we're in pretty good shape. And in the next clip we'll add in Bootstrap with jQuery to make things look a lot nicer.
Demo: Bootstrap Install
Alright, now it's time to prepare our application to look a lot better. So we're going to add in Bootstrap and jQuery. So we will save Bootstrap, jQuery, and I will also need gulp-concat for some related reasons that I'll explain in a moment. Great, that's installed, let's jump back to Sublime Text. So we're going to create a CSS task to handle our Bootstrap work, and I'm going to need gulp-concat for that, which we just installed, and what it does is, not surprisingly, concatenates files. Now to reference the Bootstrap files we're going to reference them in the node modules folder, because that's where Bootstrap installs them under bootstrap dist and we will need the bootstrap min and the bootstrap theme to pull it together. Now let's go create our CSS task. And we'll call it css, again, its function, and we'll begin like we have many other tasks by saying gulp.source and referencing config.paths.css, which we defined up above, and then we'll pipe that to concatenate, and we want to again just call this bundle but this time we're going to call it bundle.css. So our goal here is to have a single bundled CSS file that will save us some HTTP request. And we're going to place this in config.paths.dist just like our other files before but of course this will be under CSS. So that gives us a CSS task that's going to look for our paths that we defined up above in node modules where Bootstrap is placed, concatenate those, put them in bundle.css, and ultimately drop that file in the dist folder under the css directory. Now we need to go to main.js and add references to jQuery and Bootstrap so that Browserify is aware that these JavaScript files are necessary. So I'm going to do a bit of an odd trick here and say dollar equals jQuery equals require jQuery. And what this is really saying is, there's two different ways to reference jQuery, by the dollar sign, and by the variable jQuery, so I'm just setting both of those to this, require jQuery. And defining jQuery globally is just necessary because Bootstrap expects it to be there, it expects jQuery to be in the global name space. And now that we have this set up, the last piece is of course to reference our CSS bundle up here in the header. So we'll just add a reference to the bundle CSS right here. So that's all we need to wire up Bootstrap but now to be able to test it, let's put in a little bit of markup that shows it off. I'll just put in div right here--that uses a class of Jumbotron and then just says "Hello world from Bootstrap." So we'll save this and jump back over to our command line, fire up gulp again, oh, and I see a problem, we don't have our css task running yet by default. So let's jump back to our gulp file and add a reference to css right here. And now that that's added we should be able to go back over here and fire up gulp again. We have our "Hello world from Bootstrap." Looks like our styles are coming in. If we open up developer tools and look at sources we can see that our CSS file is getting bundled and all of our Bootstrap CSS is right here in one file. So we have one piece left to configure and that is ESLint so in the next clip we'll look at how we can use it to enforce coding standards and to alert us to issues in our JavaScript.
Demo: ESLint Configuration
When writing JavaScript it's handy to have a notification when we make typos or we stray outside of best practices for writing JavaScript code. So we're going to use ESLint to lint our JavaScript and keep us informed when we make mistakes. So let's type npm install and save gulp-eslint. Great, now that that's installed we'll go back to Sublime Text and we'll add our reference at the top to lint and again this will lint our JS files, including our JSX, which we'll talk about in the next module. Let's come down to the bottom and we'll create a lint task. Now here we have to be sure to return the results of this function so that we will see the output of our linting and we'll reference the JS path that we already created for our JS task since we'll be looking at JavaScript files and we want to pipe that to ESLint and then we're going to reference a configuration file, and I'm going to call that file eslint.config.json. So this is a file where we can create our rules. And then finally we'll need to see those results so we will say eslint and there's a number of different ways we could spit those out, I'm just going to use format. Now what we need to do is go create this config.json file and as you can see I have said that it's in the same directory as our gulp file, so I will right click, create a new file, and then I'm just going to paste in this config and save it, eslint.config.json. Now just quickly what you can see is I'm alerting it that we're going to be working with JSX since we're working in React and then I'm also telling it some common environments that we'll be working in such as the web browser, working with Node, and working with jQuery. And then I've created some rules that I've found handy for working with JSX. You can of course tweak these based on your environment and your own decisions about working in React and Flux. And then finally I have defined a couple globals, because again, these have to be global to make Bootstrap happy. It expects those to be out in the global name space. So this helps keep ESLint from getting upset when it sees me defining these as global. And we want our lint task to run as part of our list of default tasks and also in our watch we're going to want our linting to happen. Each time that JavaScript changes we'll want to lint as well. And now that this is wired up we should be able to go back to the command line, hit gulp, and it looks like eslint is not defined. Ah, and I know why that is. It's because I called it lint up above, not eslint. So we will save this and go back and try again. There we go, so we saw our lint task run as part of our build step and as we can see we didn't get any notifications so this is a good sign that so far our code is passing. So we're not seeing any errors after the lint process runs which tells us we're in good shape, but let's go back and create something that should upset lint. And I'm going to say test equals one, just create a global variable. And once I hit Save, if we come back over here, now we can see that we are seeing this lint error that on line three we're calling an undefined function. So "no undef" is the rule that I'm breaking in this case. So we'll come back in, and I'll go ahead and remove this, and hit Save, and then when I do, we can see now that linting is again succeeding. So now ESLint is here to help make sure that our JavaScript coding style remains clean and consistent. And this is already set up to handle JSX, which will be really handy as we jump into React in the next module.
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.
Summary
So that's it! We did it! We now have a luxurious process for building React components. If you ever want to update our dependencies to the latest version, it's as simple as typing npm update. And if we want to add other libraries in the future, we simply type npm install and add a require statement to the top of the file where we want to put the new library to work. It's really low friction. Browserify will bundle all our packages up for us and compile our React JSX, which we'll learn about in a moment. Bootstrap will make it easy for us to style our application and lay out our work in a useful, responsive grid. ESLint will help us maintain high code quality by keeping an eye on the JavaScript code we're writing and alert us to any typos or best practices and standards that we're not following. Just keep an eye on the console throughout the course for notifications. It will lint our files every time that we hit Save. Finally, gulp is the glue that ties this all together. As our taskrunner, gulp is running our builds, watching our files, firing up a web server, opening our application, and automatically reloading every time that we hit Save. Now sure, none of this was absolutely necessary to write applications in React, but my goal in this course is to give you a good feel for how it is to work with React and Flux in the real world, and these tools will help us be very productive and are commonly used by people using these frameworks and libraries in production. So that's a wrap on our environment setup. Now it's time to dive into React. We'll begin by exploring a few core technologies that make React so unique.
React Core Concepts
Introduction
In this module, we'll introduce React, and we'll learn why it's become so popular so quickly. We'll explore one of the most controversial aspects of React, JSX, and then we'll close out this short module by learning about how React's innovative Virtual DOM helps deliver exceptional performance and an excellent developer experience. Let's get started. As we discussed in the introduction, React is just one of many ways to build Components, but React contains a number of innovations that set it apart, including JSX, the Virtual DOM, and its lightweight and pluggable nature. So why should you consider React over all the other Component options out there? Well, React is known for being extremely fast and responsive. React can scale to large and complex UIs because it's very efficient about how and when it manipulates the DOM. React is designed to help eliminate layout thrash by using a Virtual DOM behind the scenes. React offers a really simple model for composing components. You can easily nest components within other components and pass data down to child components via props. These props look and operate very similar to simple HTML attributes. Unlike larger full-featured frameworks like Angular and Ember, React is easily pluggable into existing applications. Since it's just view layer, you can easily integrate it with other technologies. So you can use React in certain places to try it out, or where you feel it adds the most value, or you can, of course, create an entire app using React for the view layer, which is what we're going to do throughout this course. React components are Isomorphic friendly. This is a friendly way of saying that they can be rendered on both the client and the server. Some call this universal JavaScript. React doesn't need the DOM to be able to render. Isomorphic rendering can increase perceived loading performance, avoids repeating code on the client and the server, and it offers a simple path to search engine optimization. Since React is a very focused library, it's quite simple to learn, as you'll see. The API is small and there aren't very many moving parts and concepts that you need to master. Finally, React is battle proven. It was created by Facebook and it's used extensively on Facebook.com, which is one of the most popular and highly trafficked websites in the world, but React hasn't just been embraced by Facebook. It's been embraced by a long list of well-known companies that you probably recognize like Yahoo, Dropbox, The New York Times, Reddit, Paypal and Netflix.
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.
JSX Introduction
Let's begin by discussing the unique way that React handles markup. React offers an optional XML-like syntax, called JSX, for markup. It looks almost identical to HTML. That's why I put quotes around HTML. It's not actually HTML but it has a nearly identical syntax. There are some minor differences, such as using className instead of class, and htmlFor instead of for. Of course, a browser wouldn't know what to do with JSX, so JSX compiles down to plain JavaScript. So really, JSX is simply an abstraction over plain old JavaScript calls. Thus, JSX is totally optional. You can use JavaScript to declare your markup if you prefer, but most people prefer JSX because it looks like the final HTML, which makes it easier to write, easier to read, and more approachable for designers. Now, before I show you JSX, I want to warn you that it's a bit like the first time you see an ugly baby. At first, you're going to have to hold back a cringe, but after you give it some time, you're likely going to fall in love, trust me. Now, also, clearly, this baby's adorable. Try to imagine an ugly baby here. This is my son so I'm a little biased. Anyway, with that, let's take a look at JSX. And here we have JSX. This is a simple React component that displays an AboutPage. Now most of this is plain old JavaScript, but what's this here in the middle? That is JSX. As you can see, JSX looks like HTML but it's sitting in the middle of your JavaScript. Now you're probably wondering how in the world this works. It's not valid JavaScript as is, and that's why JSX has to be compiled into JavaScript, so that your browser can understand it. Compiling JSX to JavaScript is pretty simple. Here's a snippet of JSX. As you can see, the JSX on top looks like HTML. As I mentioned in the previous slide, there's only a few minor differences, and this JSX compiles down to plain old JavaScript. As you can see, it becomes a call to React.createElement. The function is passed the name of the tag you created, an object that specifies the attributes that you'd like to set, and finally, the markup that should sit inside. This final parameter will contain calls to other elements if you have nested markup. Let's take a look at a more complex example. Here, a table structure is compiled down into multiple nested calls to React.createElement. The XML-style syntax of JSX has the benefit of balanced opening and closing tags. This helps make large trees easier to read than function calls or object literals, and you can really see this clearly by comparing the left side to the right hand side. Notice that the brace matching nature of JSX makes it easier to read and understand than the raw JavaScript on the right. It's harder to spot a missing closing tag with the raw JavaScript because it's simply a bunch of parentheses. So yes, you can avoid JSX if you want to do it the hard way and write this JavaScript yourself, but it's harder to read, it takes more time to type, and it's much less friendly to designers, so I recommend using JSX.
HTML in JS: A Justification
Now, the idea of putting HTML in your JavaScript may seem really wrong to you at first. Doesn't this totally ignore separation of concerns? Well, let's explore that concern for a moment. If you've been doing front end development for long, you've likely worked with Angular, Ember or Knockout. They likely never occurred to you but these frameworks effectively put JavaScript in your HTML, so they're doing the exact opposite of JSX. Angular uses ng-repeat to add looping logic to your HTML. Ember uses the handlebar-style syntax to effectively add JavaScript to your HTML, and Knockout uses HTML5 data attributes in the foreach binding to loop over arrays in your HTML. So, all three of these syntaxes are functionally equivalent, even though they look very different, but the bottom line is that each of these are effectively putting some JavaScript in your HTML. These are effectively a domain-specific language that JavaScript it using to parse your HTML, and unfortunately, you have to learn this proprietary domain-specific language to understand any of these. So, you have to ask, instead of effectively putting JavaScript in your HTML, why not put your HTML in JavaScript instead? And that's just what React does with JSX. So JSX isn't really that wild of an idea. It's simply the polar opposite of the mindset that's been so popular over the last few years. Instead of trying to enhance HTML to support simple logic, data binding, and looping semantics, why not use JavaScript to define our markup? And this has some clear advantages, since JavaScript is a far more powerful technology than HTML. This means that you can enjoy all the power of JavaScript when you're composing your markup. That's a big win over the proprietary binding systems that Angular, Ember and Knockout use today. So as we just saw, we already don't have separation of concerns. HTML and JavaScript weren't ever very separate anyway. Sure, one's a markup language and the other is a programming language, which can define rich behaviors, but to create any significant app, these two technologies must be carefully kept in sync. Now this about this. In server-side languages like Java or C#, we have a luxury of using strongly-typed interfaces. These interfaces allow us to separate concerns but enforce a common interface that must be implemented, however, keeping JavaScript and HTML in sync is a tricky problem and a source of many bugs. It's tricky because there's no explicit interface between HTML and JavaScript. You have to manually try to keep these two in sync or your application will crash, and often, these two get out of sync and the application fails in unpredictable and hard-to-debug ways. For example, if you're using Angular or Knockout, the typo in the markup can cause the application to silently fail. Why? Because HTML isn't strictly parsed, like JavaScript is. That makes it a very inhospitable environment to be adding looping logic and data binding. In JavaScript, we enjoy clear error messages that point us toward the line number where the error occurred, but think about HTML. When you make a typo in HTML, things typically just silently fail. Browsers were designed from the beginning to be very liberal in what they accept. That certainly helped propel the web forward, but it's a lousy place to be writing any kind of logic because when you make a typo in HTML, you typically have no idea what line just failed. Contrast that experience with working in JSX. Since JSX is just JavaScript, when you make a typo, the JSX often won't even compile. For instance, when you try to iterate over a variable that doesn't exist, it will throw an error and reference the exact line where that error exists. If you forget to close a tag, it will throw an error on that line. This is a huge win. If you make these same mistakes in HTML-oriented frameworks like Angular or Knockout, it's very tricky to track down. JSX honors the philosophy of "Fail fast, fail loudly." This helps make debugging much easier by drawing attention to mistakes on specific lines as early as possible. So in summary, HTML and JavaScript are totally intertwined concerns. Putting them in separate files fails to really separate them. It's merely a separation of technologies. JSX acknowledges the tight coupling of HTML and Javascript, and thus, allows you to compose your HTML via JSX. As you'll see, this integration of intertwined concerns actually makes debugging easier.
JSX Editors
One quick note before we jump into code. Since JSX is in JavaScript, if you use an editor that doesn't know about JSX, you're not going to get proper code coloring, and you're likely going to see a lot of squiggly lines under your JSX because the editor will interpret that JSX as an ugly typo. So if you choose to use JSX with React, you'll want to use an IDE that provides support for the JSX syntax. Thankfully, there's a variety of options. Sublime Text is the editor that we'll use throughout this course. It has a JSX plugin that you can install. JetBrains' WebStorm comes with JSX support built in in its latest release. There's Adobe's Brackets and GitHub's Atom editor offers a package that adds JSX support as well, and finally, if you use Visual Studio, Visual Studio 2015 is also expected to offer JSX support, but the bottom line is, there's a lot of options out here for working with JSX. And, of course, if your editor doesn't support it natively, you could still write JSX, you might just have to deal with some missing syntax highlighting and code coloring.
Virtual DOM
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.
Summary
In this module, we began exploring React. We saw that React is fast, easily composable in existing applications, and a battle proven library that's used by many well known companies. We discussed two foundational concepts in React, JSX and the Virtual DOM. JSX looks like HTML in your JavaScript, and it compiles down to JavaScript. This may seem like a bad idea. It certainly did to me at first, but in the next module, we'll put JSX to work, and I suspect you'll grow to really appreciate the compile time error checking and colocation with related variables for rapid application development. And the Virtual DOM provides exceptional performance by carefully diffing current state with future state, and updating the DOM strategically in the most efficient way. As you'll see, it also enables a Simple Mental model. In React, there's a clear separation between the data and the DOM. The DOM is merely a representation of current state. This makes applications easier to understand and test, and the Synthetic Events system gives us performance for free by doing the right things behind the scenes and abstracting away browser quirks. Finally, the Virtual DOM enables other innovations like simple Isomorphic rendering and React Native, which allows you to use React to build native mobile applications. So that's React in a nutshell. Now it's time to dive in and start writing some React components. In the next module, we'll create our first components and see how easy it is to compose components together to create complex and easily maintainable web applications.
React: Creating Components Introduction
Intro
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
To get started creating our first React component let's create a folder where we're going to keep all of our components. Since we're keeping all of our source code under the source folder, I'm going to create a new folder in here and we'll call it components. And then within components we'll create our first react component. I'm going to save this file and call it homepage.js because this component is going to display our homepage information. I like to start any file with use strict when I'm working with JavaScript. This just tells the browser to evaluate everything that we're doing in strict mode. We'll need React so let's say var React equals require React, and this just imports React into this file so that we can use it. This is using the CommonJS pattern. Now we can define our React component. I'm going to call this React component home and I will call React.createClass. React.createClass allows us to define a class which contains our component and I'm using the ES5 syntax here. If you want to use ES6 that's of course fine as well but throughout the application we're going to be using ES5. And the one function that we need to define our React component is render. This is required on any React component and the render function is where we put our JSX. Whatever the render functions returns is what will be displayed to the screen. I'll say return and then I'm going to put in parenthesis. We will wrap all of our JSX within parenthesis. These parentheses are necessary if you have multiple lines of JSX that you're working with and in this case we do. I'm going to say div class name equals jumbo tron. I'm leaning on a class name that comes from Bootstraps that we can style this div. And also note that I had to say class name rather than class because class is a reserved word in JavaScript so JSX uses class name instead. When this becomes HTML it will of course just use the class keyword. We'll put a header here and say Pluralsight administration. And then let's put in a paragraph that describes our application. We're using React, React Router and Flux for ultra responsive web apps. The final piece that we need is to export this so that it can be used elsewhere in the application. To do that we use module.exports equals home and notice that I say home here because that's the variable that I set up here on line five. I can choose whatever variable that I like but typically you're going to want to use the name of the component. This is our homepage component so I just said home, I could have said homepage here as well. One other thing that's missing is a semicolon right here after our return and we'll save that. Now we can move over to the main.js file. Remember this is really the bootstrap of our application. This is our entry point for the application. At our entry point we're of course going to need a reference to react so we'll require it in. We'll also need a reference to that homepage component that we just created which is under components homepage. Finally, we can go over to our index.html and before we had some hardcoded div in here that we no longer need because our entire application is going to be served up by React now and placed within this div with an ID of app. That's why over here in main.js we need to render our application to this div. We can make that happen. We don't need these lines anymore and instead what we're going to call is React.render. React.render takes two parameters. The first is the component that we'd like to render which in this case is home and notice I just pass it JSX as that first parameter. The second parameter is the DOM element that I'd like to attach my application to. I will say document.getElementById and element is app. This is saying go take our homepage component and attach it to the DOM element ID of app over here in index.html. Now that we have this all wired up we can jump over to the command line and run gulp which will build our application and then fire it up in the browser. I will type gulp and see if everything works. And it fired up our application and now we can see our markup getting rendered. And of course if I inspect the element here we can see that this is rendered from React and that's why we get a little bit of extra goodness in here. These React IDs help React keep track of these different DOM elements that have been rendered. We can also look at the command line and see that our linting ran and we don't have any syntax errors. We're at a good spot. Now that we've created our first React component let's create another component and investigate how we can navigate between these two different pages.
Demo: Simple Routing
In the last demo we created our first React component, now let's create a second React component and see how we can navigate between these two. I'm going to create a new folder that will hold this component and call it about. Because what we'll do is as we create different sections for our application we'll put them in separate folders. We'll create a new file here and I'm going to call this about page.js. To save you watching some typing, I'm just going to paste this one in because as you can see it's very similar to the homepage component that we just created. The only real difference here is the JSX that I'm defining inside. Here I have a div and my header instead is about and then I just have a little about page here that describes the different technologies that we're using in our application. But the question is how would somebody navigate between this about page and the homepage that we created earlier because our application's bootstrap right now is just hardcoded to always show the homepage? Now of course one way that we could solve this is by bringing in React Router which we will discuss in a later module. But I want to show you first just a simple way that you can do this on a really small application without having to pull in a third party routing library. While it works, you'll probably find React Router really useful but I think it's helpful to see the simple way to get things done first before we pull in a library. I'm going to reference about right here which is in components about, about page. And then the question is when I call React.render ideally I'd be able to put in references to different components based on the URL. Let's figure out how to do that. To make that happen let's create a new React component right here that we will call our app and I'm going to say React.createClass. And then within this class we will have a render function. I'm going to create a variable here because remember within your render function this is just JavaScript so I can use any common conventions in JavaScript. This will be a variable that keeps track of which child we want to render because the child that we want to render, in other words home versus about, is going to depend on what the URL looks like. Let's switch right here. We'll create a switch statement that looks at properties and the route for this particular app. We will say if the route is about then of course the child that we want to render is about and we'll break here. Otherwise, we will just default to the homepage. And then we can finally return our markup. I will define just a div here for now and put the child right here. Reading this code it's going to look at the route which we'll figure out how to set this in a moment, but based on the route it's going to either load the about or the homepage component. We'll put a semicolon on our return. What we need to happen is when the application renders we need to watch for hashchange. So we're going to add an event listener right here and this is hashchange is the event that occurs when there's a hashchange in the URL. And then we're going to call a render function which we need to define right here. Since we said that onhashchange render needs to occur and then we're also just calling it here to happen each time but we of course need to define the render function. Now the render function's going to do two things. First it's going to get the route which is window.location.hash.substring and effectively I'm getting the route by just taking a piece of the URL right here. And then I can call React.render and instead of what we were doing down here where we're calling React.render and hardcoding in home, we can pull this out because now we're going to call our app instead. We've effectively created an abstraction that sits above the homepage. I will call app, I think I have an extra space here and then pass it route as a property. And then of course document.getElementById app, this hasn't changed, we're still going to place the application in the same spot. And I realize I did make a typo here, I typed win instead of window because we are attaching to the window and adding an event listener. One other little typo I made here is I capitalized render, it should be lower case. Let's switch that too. Now we should be able to go over to our browser and see those changes. As long as gulp does continue to run we should still see the app here but if I come up here and put in a slash and then about, we can see that now we have routing working. We have no navigation wired up yet but we do effectively have routing working using a hash style URL. If I take this back out then we end up back at the homepage. What we just implemented was a simple ad hoc way to handle routing. I don't actually recommend using this approach in production and that's why we're going to look at React Router in the next module. But for now this will be enough to get us started and helps you understand the core concepts. One other quick final note, I've deliberately not used the use strict here at the top because we have some global variables that we need to define like jQuery here within main.js. Now there is a way I could get around that by defining an IIFE but I really don't want to confuse you by adding more complexity here. Just in this one file we don't have a use strict so of course you will see that your linter will point out the missing use strict here. And that's okay for this one file. If you want to get around it you can create an IIFE and just use strict within the rest of your file. In other words, you could put use strict just within just to wrap this body of your file. And I'll go ahead and show that to you quickly. I'll create a function win. And this is of course the end of the IIFE which would sit down here. And then I would pass in window to this Immediately-Invoked Function Expression also known as an IIFE. After I've done this, of course it helps if I spell function properly. Let's try that. After I've done this I can add use strict right here instead. This still sits out in the global space but all of these is still evaluated in strict mode. The two final changes we need to make is to change our references to the global variable window to be win instead because we are passing in window as win right here to our IIFE. See window is passed in to the IIFE and then called win right here. This properly references window now. Now that we've done that, we can see that there's no more linting errors showing up in our console. Now that we've created a couple of React components let's create a centralized header that allows us to navigate our application.
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
When naming React components there's no required standard. I prefer to keep all my React components stored under a folder called components but the file naming convention that people use varies. Now imagine that I created a component called Chat App. Facebook's examples typically put the word React in the file name so they would call that file ChatApp.react.js. Some people prefer to use the JSX extension to clarify that there's JSX inside the file. Others simply name the file camel case. I find this final option preferable. Let's consider the merits of using a .js versus a .jsx extension. When creating React components, you might be torn like I was about whether to use a JSX or JS extension for your component files. As you can see, both approaches have some unique benefits. With the JS extension, all IDEs should provide code coloring for your JavaScript. However, if you choose a JSX extension, some IDEs that don't have builtin JSX support or the necessary plugins installed won't provide any syntax highlighting. Some IDEs will have no idea what a JSX file is and therefore all text will be the same color. Already most popular IDEs either provide builtin support for JSX or a plugin extension to add support. So thankfully this is already rarely an issue. Second, when you use a JS extension your operating system will recognize the file as JavaScript and it will associate the file with a default application and provide a useful icon. Files with the JSX extension may be unrecognized by your operating system which means files will have no icon or associated application by default. They require statements that you place at the top of each file to reference your components will assume that you're requiring a JS file by default. This means if you use a JSX extension for your components you'll need to type that JSX extension in to each require statement. Or you'll need to configure your build process to deal with the JSX file extensions being in your require statements. So far JS looks like the way to go but there's some advantages to using a JSX extension as well. The JSX extension makes it clear that a React component using the JSX syntax sits inside. This can help people understand the content of your files without having to open the file. The risk of confusion is minimized by placing your React components together in the same directory. The JSX extension clarifies the file's content. Since JSX is invalid JavaScript, you could say using a JS extension is lying about the file's contents. Finally, using a JS extension may confuse some editors who need to see the JSX extension to know how to handle and syntax highlight the file. However, with the editors I've used so far, working with JS files hasn't been a problem. All things considered, I prefer to use the JS extension and that's the convention that I'll use throughout this course. For what it's worth, Facebook also uses a JS extension on their components. Now that said, a number of people prefer to use a JSX extension so it's really up to you. Just be sure that you're consistent.
Summary
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.
React Lifecycle
Intro
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.
Component Lifecycle
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.
Summary
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.
React Composition
Intro
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.
Controller Views
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 Validation
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
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.
Summary
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.
React Router
Intro
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.
Route Configuration
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.
Demo: Bootstrapping
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.
Links
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.
Demo: Links
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.
Demo: NotFoundRoute
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.
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.
Demo: Redirects
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.
Transitions
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.
Demo: Transitions
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.
Locations
I have one other note before we move on to a demo of configuring routing. When doing client side routing, there are two common approaches, hash location and HTML5 history via push state, replace state and pop state. The hash location style looks like this. And it's a bit of a hack because it creates ugly URLs that have a hash in them. In contrast, the HTML5 history API keeps your URLs clean. However, the hash location works in all browsers, while the newer HTML5 history location style doesn't run in IE 8 or IE 9. There's also support missing in Opera Mini and older versions of the stock Android browser. However, nearly 90% of people globally have support for this superior history location style of URL that you see on the right. So whether you can use it or not really depends on what browsers you need to support. Finally, if you're wanting to do server rendering, also known as Isomorphic JavaScript, you have to use the history location style with React Router because the hash location isn't compatible with server rendering.
Demo: Locations
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.
Navigation Mixin
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.
Summary
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.
React Forms
Intro
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
Now we're back on the Manage Author page component. I imagine your initial reaction is likely to want to start adding a form right here in the render function. However, remember how we learned about the importance of controller views in an earlier module? When working with forms, this becomes really useful. Our top-level component for this page should have all the smarts, but very little of the markup. So instead of putting the input fields right here, let's make a call to a child component. The child component will contain the form markup. This means this top-level component will be smart. It will handle marshaling data, validating input and passing data down to the child components on this page. Let's create a child component for the Manage Author page. We'll call this child component authorform.js. That's where our form inputs will actually sit. For the author form, I'm going to paste in an empty shell, just to start us off, and save that. Then, of course, we need to go back to the Manage Author page and add a reference to the child component that we just created. Since the author form is in the same path, it's just dot slash author form. And, of course, we can add a reference to the author form right here. Okay, now that we have our controller view calling this child form, let's go over to the form itself and add in an initial form that the users can use to enter author data. Here's the initial markup that we're going to use for our form. You can see we have a header, we have a label for first name, an input, and then we break, and then we have a last name, label and the input for last name. Finally, we have a submit button that sits down here at the bottom. So nothing too surprising here, pretty standard-looking HTML. Okay, and if we check the console, we can see that I have an error. That error is that I'm not wrapping adjacent tags properly. Adjacent tags have to be wrapped in an enclosing tag in React. This is a common mistake, easy to make. So if we go back over here to the Manage Author page, and we can see that the issue is there's two top-level components here. When you work in React, you need to have a single top-level component because, remember, JSX compiles down to just JavaScript, so there needs to be one top-level function call. One way I could fix this is to wrap these two elements in a div, but I actually don't need this h1 because we're going to put the h1 within the author form. It's a bit of a judgment call, but I've just decided to keep it there. So I can save the Manage Author page, and we should be good now. Let's jump over to the browser and see how this looks. Okay, we can see our Add Author button, which we saw before, and now when we come over here to the Manage Author page, we can see our form fields and our Save button down here. So far, so good. Now in the next clip, we'll talk about controlled components because there is a quirk that you haven't noticed yet.
Controlled Components
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.
Saving Data
In the previous clip, we finally wired up the manage author form, and we utilized reusable text input components to keep that lean and pretty simple, but we still haven't configured the Save button, so let's make that happen. To get the Save button to work, let's go over to the Manage Author page. We can see that right now we're passing over an author and an on change event, but we also need to pass over some way for that author form to save. So let's declare a function that will save authors. I'm going to call it save author, and it will take one parameter, which is event, because the assumption is we'll be passing the event from that child component up to this component. Because of that, the first thing that we'll want to do is prevent default Because we don't want the default browser behavior to happen here, we're going to capture that and then use JavaScript to work with it. If we didn't prevent default, then clicking that Submit button would cause the form to actually submit on the page. And, of course, the next question is, how are we actually going to save the author? Well, technically, we're not going to save the author at all in this demo because remember, we have this author API over here. This is a mock API, it's not hitting a real database or making real AJAX calls to hit a server. What's it doing is it's just mocking that out. So what will happen is we'll call this save author method over here on our mock API, and it will generate an ID here on the client. Normally, obviously, that would happen on the server, but here we're just going to do it on the client. Then it will return the final author. So what we need is to add a reference to the author API. So we'll add that right here. Now that we have a reference, we can go back down here and put it to use. I'll just call the author API and say save author, and then pass it the state of the author. So this call should end up saving our author to our mock API. Of course, we also need a comma here, separate our function. Now that we have a save author function, we'll want to pass this down. We'll call this attribute on save. I will just reference the save author method that we just defined up above. This means of course, that we need to put this to use over on the author form. Since we're passing down a function right here, I can say on click, and then reference this.props.on save. So this is now referencing the function that's getting passed down. If you think about this, it's pretty useful because now this author form component is very reusable because we're passing in change handlers from a parent, we're passing in a click handler from the parent. So there might be other places that I'd want to use this from, but I might want to use a different save method or different change handlers for these inputs. This is the beauty of having a separation of concerns between a controller view, which, in this case, is the Manage Author page, and the author form. Which in this case doesn't have any logic. It just defines some JSX that handles our markup. Now that we've wired this up, let's go over to the browser and see how this works. Now I'm just going to enter John Smith. Then hit Save. Now, I don't get any kind of feedback at this point, which isn't very helpful, but if I click on Authors, we can see that our save worked. John Smith is now listed here as one of the authors. So we really just have some tweaking here to make that experience more polished for the user. What should probably happen is I should be redirected to the list of authors after I click Save. So in the next clip, let's see how we can use React Router to redirect the user over to the list of authors after they save an author.
Programmatic Redirects
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.
Notifications
In the previous clip, we set up a handy redirect, so after the user saves an author, they're taken back to the list of authors, but they're still not getting any nice notification. I've moved back here to the command line because now we need to install another library. There's a lot of different ways that I could notify the user that something occurred, but one of my favorite ways to get this done is the library Toastr, which you might have heard of if you've watched any of John Papa's courses. So I'm going to use MPM to install Toastr. I'll say MPM install, save Toastr. Great, now that it's installed, let's put it to use. There are two pieces to Toastr. There's a JavaScript library and CSS that comes down with Toastr. We can see that if we go into the Node Modules Folder and look under Toastr. We can see the Toastr CSS and the Toastr JavaScript. So first, I'm going to wire up the Toastr CSS. To do that, let's go into the Gulp file. In our Gulp file, we already have a couple of CSS files referenced here, but let's just add another one in here for Toastr. So now our Toastr CSS will end up bundled with the rest of our CSS. And now that we have this added in, we can go to the Manage Author page to wire up the rest. The first step is, of course, referencing the Toastr library. So I will call it Toastr. Then it's just a matter of putting it to use right down here. So right before we do the transition, I'm going to use Toastr. I'll just make a call to it and say success. With Toastr, it is a really simple API. I can say success or error, and then I just pass it a string, and it will take what we've put here, which I will say Author Saved, and it will display this in a nice, friendly message to the user. Now, before we can test this, remember, we need to make sure Gulp is running because if you're like me, you might have killed your Gulp process so that you could install this. So we need to go back to the console and type Gulp again so that it will reopen our application with the latest build. So let's try this out and see if it worked. We'll go over to Authors, click Add Author, and we'll put in an author. Might as well put John Papa, since we're using one of his libraries, and we'll hit save. Now we can see the author was added in, and we get this nice Toast sitting up here on the top of the screen. So we're communicating well with the user about what just occurred. However, there's another issue that we still haven't handled, and that is validation. So let's look at a simple way to handle validation when working with React forms.
Validation
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.
PropTypes
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.
Transitions
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.
Summary
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.
Flux
Intro
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 handle data flows throughout our application using Flux. Now Flux is not a framework. It's really just a name for a pattern of unidirectional data flows. This is why you see so many alternative implementations of this pattern with slight tweaks. As you'll see with Flux, all updates to your application's state occur via a centralized dispatcher. This dispatcher, not surprisingly, dispatches data to your application's data stores. Now this pattern enforces a clear, predictable, unidirectional data flow. Unlike popular two-way binding libraries like Angular and Knockout, data flows in one direction in Flux. We'll see how this works in a moment, but in short, you're going to need the Flux dispatcher and a JavaScript event library. Remember how I joked that they call it React because it creates such strong reactions in people? Well I'd say the name Flux fits well because there's so many alternative implementations that are very similar in concept, but differ in various ways. Okay, really, the name Flux makes sense because Flux deals with actions and data changes. Before we dive in, I want to clarify that this module discusses Facebook's Flux implementation. Flux is Facebook's name for an architectural pattern that has unidirectional data flows and a centralized dispatcher. There are many alternative implementations to Facebook's Flux, all with fun names including: Alt, Reflux, Flummox, Marty, Fluxxor, Delorean, Redux, NuclearJS, and Fluxible. Now in this module we're going to discuss Facebook's Flux implementation because it's popular and battle tested in production on one of the highest traffic sites in the world. It's also the inspiration for other implementations that you see here. That said, you might see some things you don't like about Flux. There's admittedly some boilerplate and plumbing code necessary, but I will say that once you get used to the pattern, it's very easy to understand and extremely scalable. Regardless, the great news is that if you decide Facebook's Flux isn't for you, there's a wide variety of alternative Flux implementations out there that work well with React. Generally these Flux alternatives are quite similar in philosophy and API, but in many cases require less code to do the job, often at the expense of either clarity or flexibility. Have you ever built a large client-side application that requires interactions among multiple views and models? As applications grow you may find the traditional MVC pattern devolves into models and views that end up interacting with one another. You may find you need to pass data between your view models, but doing so makes your application difficult to reason about and thus very unpredictable and tricky to debug. And this leads us to the sales pitch for Flux. Facebook ran into these issues and chose to use a unidirectional data flow as a solution. Let's contrast two-way binding to unidirectional data flows to help clarify how this works. As I said, Flux implements a unidirectional data flow. That sounds complicated but it really just means the data flows in one direction. I find this easier to understand by contrasting the unidirectional data flows with two-way binding that you're probably already familiar with. So if you've used Angular, Ember, or Knockout, you probably recognize this model on the left. When you change the value of a text box, the corresponding data behind the scenes is updated immediately. It's quite handy and it's simple to understand. In contrast, with Flux, an action occurs. And a dispatcher notifies any stores that have registered with the dispatcher that an action has occurred. When the store changes, the React component is updated so the user can see the updated UI. When the user interacts with the UI element, a new action occurs and this unidirectional flow starts over. This is called unidirectional because updates flow in one direction from the action ultimately to the view. Unlike two-way binding, the view doesn't directly update state. Instead it fires off actions which ultimately update that state. Unidirectional data flows make the application easier to reason about because the results of a given action are easy and predictable to trace. Unidirectional flow does require some extra concepts and code as you're going to see, but I find that it's at the benefit of clarity, testability, and predictability. So you can think of the trade off like this. Two-way binding is simpler conceptually and requires less typing but unidirectional flows pay off as your application grows because it's more explicit and it makes it easy to update multiple stores when a given action occurs.
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
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.
Dispatcher
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
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.
Controller Views
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.
Flux API
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.
Summary
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.
Flux Demos
Intro
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.
Dispatcher
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.
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.
Stores: Registration
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.
Stores: Interactions
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.
Stores: Initialization
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
In the previous clip, we set up our ChangeListeners, so I'm going to save those changes and now we'll put them to use by opening the author list component. What we'd like to do in this component is allow deleting an author, so what I'm going to do is paste in another TD tag here and this will give us a link to delete an author, and, as you can see, we're calling the deleteAuthor function, which I need to define. But, before I do, of course now our table has another column so I will need to add it down here in the head. Let's define our deleteAuthor function up here above render and I'll just paste it in and then we can discuss it. You can see that I'm calling deleteAuthor and I'm binding this and then passing in the current author ID, so this ends up populating the ID and then the event comes in as a second parameter. The first important thing to do is prevent default because we don't want the browser to do anything when I click this link, we want to trap it and use javascript here on this next line to fire off our action. And the author action that we're calling is deleteAuthor, so our next step is to go over and define this action. Of course, the other thing that we did here was we called toastr so that we could display a message to the user that the author was deleted. Of course, we also need to add a couple of require statements here at the top because we are calling author actions and we're also using toastr. There's a couple of ways that you could tweak what I just did. The first thing is you might be thinking, "Shouldn't the deleteAuthor function be defined "in the top level controller view "and passed down to this author list?" Yes, we could of course do that. Part of that decision comes down to how likely it is for this component to be reused and in what way. By defining deleteAuthor here we make it easy to use this functionality in other places, assuming that the deleteAuthor logic is unlikely to change elsewhere. Here, the logic is very simple. It's simply calling an action, so I feel it's okay to handle deleteAuthor here. But, of course, if you want no logic in your child components, you could simply pass this function down from the parent via props. Now, the final missing piece is we haven't defined our delete author action, so let's quickly wire that up, come over here and add a constant for DELETE_AUTHOR. Now, that we've defined our constant, we can go over to our authorActions file. I'll just paste in our deleteAuthor function. It's going to look very similar to our others, but you can see that instead we call the AuthorApi and delete the author and then we fire our DELETE_AUTHOR action, and I'll save this. One other interesting point here, if you're wanting to make asynchronous calls to the server and show a preloader in your application, one easy way to do that is to have a separate author deleted action that fires as well. So, for instance, if this were an asynchronous call, you could immediately fire your DELETE_AUTHOR action, and then once this asynchronous call completed you could fire off an author deleted action. That way all of your UI would be aware that there were an asynchronous call currently in process and once it received the author deleted ActionType, it would know that it should hide any preloaders and show any final confirmations. But for simplicity here, I've just stuck with the synchronous setup and we just fire off our deleteAuthor action after the API call has been made. Of course, there's one final piece to set up and that's our store, so let's go to the AuthorStore, and down here we need to add a new case statement for deleting an author. To do that, let's just paste in another case statement right here. So, as we can see, to delete an author I'm just going to use lodash to go find the author that has the ID that we've been passed and then, of course, emit the change when I'm done, so it still looks a lot like our other handlers defined above. And I'll save this change. And now we should be ready to test this out. Let's go over to the browser and when I click delete the authors go away. But please, don't delete me. My next course will be better, I swear. All right, so we have it all wired up now. Our deletion's working, our listeners are listening to the changes on the store. And to clarify how important those listeners are, let's go back for a moment and comment those out. If I go back to the author page and I comment out these ChangeListeners and hit save. Let's go back to the browser. The browser will refresh because I just made changes, and now when I click delete the author isn't actually deleted. Now, we made the call to the API. That deletion call went through, but, since our React component, which in this case is the author page, isn't actually listening to changes on the store, the UI never reflects that change. So, we have to have these functions set here so that we're aware of the store changing, and that way our UI will be updated accordingly. To drive that point home, in our final clip let's set breakpoints on each of the steps in this flow and watch our data move through the unidirectional data flow pattern that Flux implements.
Summary: Stepping Through Flux
To quickly show the entire flow of Flux, let's set breakpoints at each of the steps so we can watch our data flow through the application. We'll begin by going to the author list and this is the first action that gets called when we delete an author, so I'm just going to type debugger right here. What this does is it sets a breakpoint in our browser right here, so I don't have to go over to the browser and find this line in our unified javascript file, Chrome will just break at this point for me, so it's a nice little shortcut. We're obviously calling deleteAuthor action here, so let's go over to our author actions and we'll set a breakpoint right here as well. From here, we're dispatching our action so this should land over in our store within this case statement, so we'll set a debugger here. Then our author page is listening to our store for changes, so we should see this get called as our store changes. All right, let's jump over to the browser and see this whole flow in action. I'm going to hit F12 to open up the console and I'll click delete. We immediately hit our first breakpoint. This breakpoint of course is within the author list component and we're right above that call where we fire off the action, so we're going to make a call to the deleteAuthor action. I'll hit F8, and now we've stepped to the next point in the process. Now, we are in our AuthorActions file and we're within the deleteAuthor function, so we're about to call the API and then dispatch our deleteAuthor action. I'll hit F8 again. Now we land over in the store so it received that action which was just dispatched and that action had an action type of deleteAuthor. So now we remove the author from the list of authors and then we emit a change. This emitting change is important because this informs any stores that are listening that a change at the store just occurred. And if I hit F8 again, we can see that happen. We land in the _onChange handler for our author page component, and this is where we set state by querying to get all authors from the AuthorStore. Since we know that the AuthorStore was just updated, we just asked that AuthorStore for the latest author data. So, of course, when I hit F8, now finally that author that we just deleted is removed from the UI. So that's a wrap. We just stepped through the complete unidirectional dataflow that's implemented via the Flux pattern. And now to wrap up the course, I want to close with a short challenge to you.
Final Challenge
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.
Course author
Cory House
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.
Course info
LevelIntermediate
Rating
(1197)
My rating
Duration5h 8m
Released12 Aug 2015
Share course