What do you want to learn?
Skip to main content
Building Applications with React and Redux in ES6
by Cory House
Learn how to use Redux, React Router, and ES6 to build a real world app with React. Use Webpack, Babel, ESLint, npm scripts, Mocha, Enzyme, and more to build a rich, one step, custom React development environment and build process from the ground up.
Resume CourseBookmarkAdd to Channel
Table of contents
Hi! I'm Cory House. I really love React. And if you're watching this, I'm betting that you do too. Now, I assume you already know the basics of React. Yet, React presents a simple problem, decision fatigue. You may understand the basics of React, but can you build something big, complicated, interactive, and testable with it? React is just a library, so chances are you're going to need a variety of other tools, libraries, and patterns to build something real. Redux is one such library. Redux has eclipsed a long list of alternatives to become the de facto Flux implementation. So we're going to explore Redux from the ground up, and you'll see why it's been so widely embraced. Yet even after you've chosen well with React and Redux, you still have two hard problems to solve. Problem one is what libraries should I compose with these two technologies? And problem two is once I've decided, how do I connect it all together in a way that makes sense and is maintainable and testable? This course answers both of these questions in a comprehensive manner. We'll build a real-world style React and Redux application from the ground up. With one command, we'll lint our code, run our tests, transpile ES6 to ES5, bundle, minify, start up a web server, open the application in the default browser, and hot reload our changes all along the way. Trust me, once you experience React development this way, you won't want to go back.
Hi there! I'm Cory House, and welcome to Building Applications with React and Redux in ES6. I'm seriously excited about this course. This comprehensive course is the culmination of many hard months of research and work, and I hope that it helps you radically improve your React development practices. ES6 with Babel and webpack have become practically the de facto way to build React apps, and Redux, only just created in late 2015, has already become the de facto Flux implementation. So, in this course, we're going to explore the most popular and powerful stack for building React applications today. Now before we get started, I love feedback along the way. I'm active on Twitter as @housecor, and you can also find me at reactjsconsulting.com. This course is broken up into 14 short modules. Most are 20 minutes or less. In this short intro module, I'll quickly discuss the intended audience for this course so you can decide if this is the right course for you. We'll consider why we're exploring Redux instead of the long list of Flux alternatives. As you'll see, there're a number of great reasons to be excited about Redux. In the second module, we'll set up an awesome development environment for building React apps. With one command, we'll run our tests, lint our code, transpile ES6, and much more. Trust me, this is a seriously fun way to do development, and once you've experienced it, you won't want to go back. In module 3, we'll explore the many approaches to creating React components, as well as the differences between container components and presentation components. Once all this goodness is set up, we'll build our initial app structure and set up a React Router so we have a useful app to work with. And after setting this solid foundation, we'll be ready to explore Redux in detail for the next seven short modules. I'll introduce Redux in module 5, and I'll contrast it with Facebook's Flux. In module 6, we'll discuss unidirectional data flows, immutable stores, and reducers. Then in module 7, you'll see how to connect React to Redux. At this point, we've pretty well wrapped up discussing concepts, so the rest of the course we're busy coding. In module 8, we'll create our first Redux code by implementing the Redux flow to handle creation of an example course. Then in 9, we'll learn about handling asynchronous calls in Redux. We'll follow that up by handling asynchronous creates and updates with Redux as well. In 11, we'll polish off our application's user experience by implementing a pattern for handling asynchronous status. And in modules 12 and 13, we'll shift our focus to testing React and Redux. I deliberately saved these modules for the end to avoid adding confusion along the way. But as you'll see, both React and Redux are very friendly to automated testing. And in the environment setup module, we'll set up the basics for being able to run tests. So if you want to do TDD throughout the course, you'll be free to do so. And in the last module, I'll show you how to create a handy and powerful one-step automated production build so you can deploy high performance React and Redux applications in the real world using just three static files. And I'll close out the course the same way that I did my React and Flux course. I'll issue some specific challenges that put your newfound skills to use by attempting to enhance the app that we're creating throughout this course. And if you're someone who prefers just following demos, these are the modules you'll want to look for. The ones that aren't marked demos are primarily slides. Those that are marked demos are primarily code.
Who Is This Course For?
I don't want to waste your time, so I want to clear this up right now. This is not an introductory course. I assume that you already know React. Now if you really like learning by example, you can likely get a lot out of just following along. But if you're totally new to React, then I already have a course for you, Building Applications with React and Flux. This is my intro course on React, and it covers Flux and React Router in later modules as well. That said, these four modules will give you a solid foundation on React in about an hour. If you watch those, then you can come back here, and you should be able to follow the rest of this course just fine. Now you don't have to know Flux to learn Redux, but Redux certainly builds on Flux's unidirectional data flow pattern. So if you already know Flux, you'll find it easier to quickly pick up Redux. I will make comparisons between Flux and Redux in this course, but you do not have to know Flux to follow along. That said, if you'd like to get up to speed quickly on Flux, I suggest checking out the short 20-minute module in my Flux course. You also do not have to know ES6. It's okay if you're not yet familiar with all the new ES6 features. In fact, this course is a great way to learn by example. I'll provide short explanations of the new ES6 features that I use throughout this course along the way. We'll use a number of new features like modules, let and const, enhanced object literals, default parameters, template strings, classes, arrow functions, promises, destructuring, and the spread operator. So this course is a great way to learn ES6 by example if you're not yet up to speed.
How Is This Course Different from the React and Flux course?
In this short introductory module, we discussed why Redux is worth learning. We saw that Redux has become React's most popular data management library, and we touched on a number of good reasons why. In the next module, we'll set up what I like to call the React Slingshot development environment. We'll put webpack, Babel, Mocha, ES6, and a variety of other technologies to use to build a powerful React development environment from the ground up.
Versions Used in This Course
This course currently uses the following versions. The package.json that I'm sharing with you will install these exact versions so that I can ensure that you can follow along with me. Now since Babel requires multiple files, I can't list a single version here, but we'll be working with Babel 6. Be sure to check the package.json file for the specific versions that we're using for other dependencies in this course. I'll also keep this slide updated if I update this course to use newer versions in the future.
React and Redux have both become well known for their powerful hot reloading capabilities. Now hot reloading is an area that's likely to change again in major ways in the future. There are currently multiple ways to handle hot reloading in React and Redux applications, but for this course, we're going to use the babel-preset-react-hmre. In short, this is a Babel preset that wraps up a number of other libraries and settings in a single preset that's pretty easy to set up. It works by wrapping your components in a custom proxy using Babel. The proxies are classes that act just like your classes, but they provide hooks for injecting new implementations. So when you hit Save, your changes are immediately applied without requiring a reload. But I do want to share some warnings about hot reloading. First of all, keep in mind that this is experimental even though many people are using it in development. There are likely to be better ways to handle this in the future. And there are some known limitations, like it doesn't hot reload functional components unless there's a class somewhere up the hierarchy tree, and that's where you'll see that I'm using classes at the top of my tree so that I can make sure that hot reloading works even though in some cases we have functional components nested within them. It also doesn't hot reload container functions like mapStateToProps, which you may not be familiar with yet, but we will be discussing here soon. And finally, keep in mind that there are other ways to do hot reloading, but I won't get into the alternative approaches in this course. Bottom line is hot reloading is likely to become more elegant and integrated into React in future versions. But for now, this is my preferred way of handling it. I find that the limitations that I've listed here aren't really that big of a deal in practice because keep in mind we're only doing this in development, and the worst-case scenario is that I just have to hit Reload on my page. But when it works, boy, it is good magic that helps me save time during development.
To get started on our environment setup, let's install the latest version of Node. I prefer using the 6.x branch so I can enjoy the latest features, and the 6.x branch loads modules four times faster than the 4.x version, but the 4.x version will work just fine for this course as well. Just make sure that you're running at least version 4. Now if you already have Node installed and you're concerned about upgrading and breaking existing projects, keep in mind that you can run multiple versions of Node using nvm on Mac or nvm-windows on Windows. I won't show the actual install process here since that's very straightforward, but just click on the version that you choose and install it.
Instead of installing all packages one by one, I'm going to do you a big favor and share a package.json file with you to get you started. This will save us a lot of time because we won't have to manually type the names of all the packages that we're using. Now as you look through here, I know this is a long list of packages. Please don't be overwhelmed. Yes, these are all the packages that we're going to use throughout the course, but most of this is optional tooling that I'm pulling in so that we can enjoy a really awesome dev experience. The list of production dependencies is actually quite short. You likely don't recognize some of the packages that we're going to install, and that's okay. I'll introduce them as we go along. But if you're curious about these, you can go to the starter kit for this course, the Pluralsight-redux-starter, and scroll down, and I've listed the description of all the dependencies that we're using in this course. So let's grab the raw version of this package.json. I'll just copy it and then move over to our editor. So here I am in WebStorm, and I'm just going to create a new file, and we'll call it package.json. And I'll just paste this in here. Now one thing I am going to do is delete the scripts section here because we're going to build that out step by step in this module. Now seems like a pretty good time to talk , so let's go ahead and do that in the next clip.
In my last course, I used Sublime, but my favorite editor for the front end is now WebStorm. I've found that it has better ES6 support than any editor that I've tried. And believe me, I've tried just about all of them. One thing I really love about WebStorm is the built-in terminal. I'm going to hit Alt+F12, and you can see that I get a Bash terminal right here within the editor. As I'll show throughout the course, having a terminal that's built into the editor is quite handy. We'll rely on the terminal pretty heavily as we're doing React and Redux development. Now I will say one big downside to WebStorm is it is not free. But the good news is there is a free 30-day trial on jetbrains.com for WebStorm. So sure, WebStorm's great, but I will say there's another editor that I like nearly as well, and the great news is it's totally free. And that is Atom. Atom is excellent, and it's very extensible. So if you decide to use it, follow along. There're a couple of packages that I'd also recommend installing. And by the way, as I'm switching over to show you Atom, one thing I want to mention is both Atom and WebStorm run on both Windows and on Mac. The two packages that I'd recommend installing are react and terminal-plus. As you can see, react will give you language support, indentation, auto completion, and snippets. Terminal-plus will give you a built-in terminal that's very similar to what I showed you in WebStorm. I find this really handy. Now of course, you can always Alt+Tab or end up splitting your screen so that you can use the native terminal for your operating system, but I really like having it built into the editor this way. One other thing to note is, of course, if you're on Windows, then you will get a native Windows terminal here instead of a bash prompt. Really, Atom is an excellent editor, but the reason that I use WebStorm is it currently offers the best ES6 support that I've found. It also offers powerful refactoring tools. It shows me when I have unused imports, and it lets me jump to definition with a single keystroke. Also, its auto completion support is fast and excellent. And, of course, there are plenty of other interesting editors out there that offer a great experience in React, but these are my two favorites that I recommend trying out for this course.
Install npm Packages
Now that we have a package.json file in our project folder, let's install the necessary npm packages by typing npm install. Now I'm going to type npm install here on the terminal, and it'll start installing, but I could've just as easily used the terminal that's built into my OS. It's totally up to you. It's going to work the same way. Although I will say one little quirk that I've found by using the built-in terminal in WebStorm. There is a bug with the starter kit in WebStorm using the terminal on Windows. What happens is when you hit Ctrl+C to try to kill the process in Windows, that often does not work, so you'll have to actually click this X to close the terminal and then open it back up again. This only seems to happen with WebStorm in my testing. But otherwise, this should give you a completely native feeling experience. Running npm install will probably take a couple minutes because there're a lot of packages to install, Sso I'm going to use the magic of fast forward to get this completed. Alright, great! Looks like that completed successfully. I'm going to scroll back up because one thing I did want to mention was right here at the top when it started installing. You may see a few warnings here, and this will depend on your system. They're often optional dependencies depending on the operating system. But you can ignore these warnings. Everything should still run just fine for you. And now that we've run our npm install, we can see that we have a node_modules folder with all of our Node packages sitting inside. So now we're all set to begin building out our development environment using npm scripts. In the next clip, I'll introduce how to work with npm scripts.
Introduce npm Scripts
In my previous course on React and Flux, we used gulp for the build process, but the React community has largely embraced using npm scripts instead. So in this course, we're going to use npm scripts. Let me talk about a few reasons why I've switched. I found that npm scripts were really easy to learn, arguably even easier than gulp, and I appreciate the simplicity of using npm packages directly rather than using them through the abstraction of a gulp plugin. Eliminating that extra layer of abstraction helps me understand better exactly what my build process is doing. I also liked eliminating my dependence on plugin authors. This means that I don't have to wait for someone to create or update a gulp plugin when a new tool or version is released. With npm, I use the tool directly. I find that this makes debugging easier. In gulp, I was often trying to determine if the bug was in the underlying tool, the plugin, or my implementation. With npm scripts, you have one less point of potential failure. I find the documentation is also easier to work with. When working in gulp, I'd often have to switch between the gulp plugin's docs and the underlying tool's docs. With npm, I have one place to check for all my docs. I don't have to glue two different documentation sites together in my head. If you're interested in reading more about why I switched from gulp to npm, hit this link. And if you're not yet familiar with Node and npm, that's okay. I'll fill you in as we go along. As I said, it's easy to learn. Npm scripts allow you to make command line calls, utilize npm packages, or even call separate scripts that use Node, so they'll give us all the power that we need for creating a robust build process.
Create src Directory
Set up Webpack
Set up editorconfig
One thing that you'll notice on the previous clip is that WebStorm defaulted to using large indentation when I pass the file in. I prefer to use only two spaces for indentation rather than tabs for this course just so I can save us screen real estate. So I'm going to add an editorconfig file to the root of the project. To find that, let's go over the starter, and you can see there's a .editorconfig right here. I'm going to take the raw version of this, copy it, and paste it over into our project. And I'll just put it right here in the root of our project. I'll call the file .editorconfig. And it needs to have that exact name because our editor will look for it. So you can see the rules that I'm setting here. I'm setting indentation style to space. I hope I'm not offending anybody. I'm not making a religious claim here. This is just what I happen to use for this course. I still love plenty of people that really enjoy their tabs. Okay, so the indentation style I've set to just two spaces. You can see I've set charset in here and some other just minor rules in here. Feel free to fiddle with this, but I bring this up too because this is really useful when you work on a team that might be working in different editors. I work on a team where some people are in Visual Studio, some are in Atom, some are working here in WebStorm. But by using an editorconfig, we can all have consistency in our spacing.
Set up Babel
Of course, since we're using ES6 in this project, we need to transpile down to ES5 in order to ensure that our code runs in all recent browsers. To do that, we'll use Babel. Babel is configured via .babelrc, so let's create a .babelrc file in the root of the project. And again, this naming is important, so make sure you name it exactly this and that you place it in the root of your project because that's where Babel will look for it. To get the contents for this, I'm going to go back out to the starter and, again, pull down the raw file, copy it, paste it in, and I'll save this. There's not much to talk about here. It's nice how simple this works out. What we end up saying is that we're going to be working with the React preset and with the ES2015 preset. This preset says that we want it to transpile anything that was part of the ES2015 standard. And then the other thing that we do is only in the development environment, that's why we put this underneath the environment property here, we want to run the react-hmre Babel preset. We can see that this exists over here in our package.json that we installed babel-preset-react-hmre. What this is is a preset that bundles up a number of different hot reloading-related code and puts it into one convenient package. Now I will say this is considered experimental code, and it is likely to change sometime in the fairly near future, but it does, from my experience, work really well, and I think you'll find it a lot of fun having a hot reloading development setup during the course. And as you can see, it wasn't too much work to make this happen, though I will concede there are some other moving pieces that we're going to wire up to make sure that hot reloading works properly throughout, and I'll try to mention those as we go along. There're also some good boilerplates specific to hot reloading that show you just those pieces running together so that you can get a good feel for how to get hot reloading going without the other complexities that I'm going to show as we build out our development environment here. And now that we have our babelrc file set up, we can start thinking about setting up a development server. For our development web server, we're going to use Express. And in the next clip, we'll see how easy it is to configure Express to serve up our application for development.
Set up Express
I like to keep all my build tools in a single folder, so let's create a folder called tools at the root of the project. Now make sure that you create this at the root. Don't accidentally nest this within a file. We'll call this tools, and you can see it's out here at the root at the same level as src. If I collapse src, it just sits there as a peer to src. Inside this folder, let's create a new file, and we're going to call this file srcServer.js. This file will configure our web server that serves up the files in our src directory. Again, you don't have to type this. Let's just grab it from the starter kit. So we'll go to tools, and here's my srcServer. View it in raw, and I'll copy this and paste it in. So it's about 30 lines of code. Let's talk this through. There're a number of options for setting up a dev server, but I chose Express because it's popular and easy to configure to work with our webpack development middleware. The first thing we do is create an instance of Express, and we call webpack with the config that we defined in the previous clips. And now it's time to begin configuring Express. We're going to use webpack's dev middleware and pass it our compiled webpack configuration. And then we specify a couple of things that we don't want information on the command line as it runs. And also, we pass it the publicPath, which we defined within our webpack.config. Then we specify that we want to use webpack's hot middleware, and again, we pass it the compiled webpack.config. Now it's time to tell Express what files we want it to serve. Since we're creating a single page app, we want Express to serve up our index.html for all requests, so we just specify a wildcard right here so that any requests that it receives end up returning index.html. Finally, we start up Express down here at the bottom listening on the port that we configured at the top, which is port 3000, and we open the browser using the open package, which is also out on npm, one of the many packages that we installed at the beginning of the course when we typed npm install. This completes our Express configuration. Now we're nearly ready to start up our flashy new ES6 development environment. But to make that happen, let's set up a script in package.json that will start up our app.
Create Start Script
Create Start Message
I also like to fire off a nice descriptive start message when our build begins, so let's add a prestart step. By convention, npm will run this script before our start script because it's prefixed with the word pre. We could also run a script after our start script if I added a script called poststart. It's a simple, powerful convention. And I'm going to call a separate file because I like a little bit of flash. So let's go over to the tools folder and create a new file and call it startMessage.js. I'll just paste this in. We're importing from colors because I want to add a little bit of color to our console.log statement. That's the whole reason I chose a separate file here, so that I could add this goodness. So we're outputting a short message to the console when we run npm start. We're also disabling ESLint's check for use of console because in this case it's okay. It's just in a build script. So let's go back to package.json and put this to use. Since our script still contains ES6, we will use babel-node to run it, and we will just call it over in our tools directory. So now we'll get a start message each time that we run our start script. Let's see how it looks. And there it is, starting app in dev mode. Just what we wanted to see.
Set up ESLint
To help us quickly catch mistakes, maintain consistency, and enforce best practices, we're going to use ESLint to lint our code. Every time we hit Save, it will run. To configure ESLint, we need to place an eslintrc file in the root of our project. And this file is out here in the starter kit. So I'll just pull it up in raw and then create the new file in the root of my project. And it needs to be called .eslintrc. Make sure it has that exact name. I'll just paste this in. As you can see, the eslintrc is just a chunk of JSON. We begin by extending ESLint's recommended settings as a baseline, and we're also augmenting the recommended settings with plugins that provide enhanced linting for ES6 imports. Then you can see down here below that I override various settings based on my personal preferences. I'm also using ESLint plugin React to add a number of useful React-specific linting rules. All of these are prefixed with the word React down here at the bottom. Zero means off, 1 means warning, and 2 means error. So if you feel strongly about a rule, you can set it to 2 and break the build. As an aside, think carefully about when to use warning. I've found teams can quickly get comfortable ignoring linting warnings. But if you make them errors, they'll have to fix the issue before they can move on. Well, unless they disable the rule, but then, of course, you can group together and beat that person with a whiffle bat. Anyway, as you can see, there are many settings in here, but these are my personal preferences. So back here at the top, ESLint recently added support for ES6 out of the box. This parserOptions section enables ES6. As you can see, we're telling it to also support JSX. One final section we didn't discuss. The environment section declares some different environments ESLint should be aware of. Each of these environments tells ESLint to expect certain global variables. We're working in these. Let's run it via an npm script. We'll come over here to package.json and add another line, and I'll paste in our new script. We could simply run ESLint directly in an npm script, but ESLint lacks watch functionality. So instead, I'm using a handy npm package called eslint-watch, which has an executable called esw. Eslint-watch wraps ESLint and provides file watching functionality. It also offers enhanced command line output. And you can note that I'm specifying the path here to the binary, but this shouldn't be necessary. I'm just doing so because I've heard stories of some people having issues in the past without it. I'm telling eslint-watch to look for webpack.config files and any files in our src directory or our tools directory. Let's open up the console and see if this works. Just type npm run lint. And when we do, we can see that we get an unexpected console statement. So we know our linting is working, and what it's looking at is in our index.js file. You can see that we're making a console statement here, and that's one of the things that we're linting. We have a no-console rule in our eslintrc file. I could come over here and just comment this out and hit Save. Then if I came down here and reran our linting, we can see that now it comes back clean. So good! Our linting is working properly. I'll go ahead and put this back for the moment. Let's go back to our package.json. Now I'd like to create another handy script. Oddly, eslint-watch doesn't watch files by default. You have to pass it a command line flag to tell it that you want to enable file watch. So let's create a separate npm script that will watch our files, and we'll call it lint -- --watch. This syntax looks weird, so let me explain. We're just running the lint script above, but this syntax lets me pass the watch flag to our lint script. So this is saying run the npm lint script, but pass the watch flag to eslint-watch. And now let's run the watch script. And now it is watching our files. So if I came over here and commented this out and hit Save, the moment that I hit Save, we get a report down here that now everything's clean. I could undo my change and hit Save, and this comes back. So now we have live reporting on the status of linting all of our files. This is handy because now we'll know when we make many common mistakes in our code and will find out immediately by just keeping an eye on our terminal. Of course, there's one obvious piece missing here. I want the linting to run every time that we start the app. So in the next clip, let's do that.
Create Parallel Scripts
We have linting working, but my goal is to run linting automatically when we start the app and to display the output to a single command line, so we need to start linting as part of our start script. To make that happen, I'm going to use an npm package called npm-run-all. It supports running multiple npm scripts and returning all their output to the same command line. Quite handy! With npm-run-all, we can run the scripts one at a time or in parallel. Let's begin by updating our start script. I'm going to move what's currently here over to a separate dedicated script that I'm going to call open source. Now this is a lot like refactoring to separate well-named functions. It's best if each script has a clear name. Since our start script is now going to do multiple tasks, each task that we'll call is going to be a separate script. So you can see I'm calling npm-run-all, and I'm passing it the parallel flag, which tells it to run anything that I list over here to the right at the same time. So we'll be running open:src and the lint:watch task. This means that when we type npm start it will display the start message because that's in prestart. It will run webpack, it will start our development web server, it will open our app in the default browser, it will lint our files, and it will rerun webpack and ESLint any time we hit Save. That's a lot of power with so little code. So now when we come down here and say npm start -s, we'll see it starting our app in dev mode. We'll see it jump over to the browser, open our app in the browser. We'll see our linting run as well. And all of this information comes down right here into the console. Quite handy! One-stop shopping for the information that we need. And before we wrap up, just two quick items of note. If you're on Mac, you may find that you have to hit Ctrl+C twice to be able to kill this process. There's actually a ticket open right now against eslint-watch that I'm hoping will get resolved that will fix that. And if you are on Windows and you're running in WebStorm, you may have to hit the red X button right here to kill the process. This is just a little quirk with the interactions between npm-run-all and eslint-watch. Alright, so to wrap up our development environment build-out, let's set up automated testing.
Set up Testing
I originally planned to discuss testing throughout the course, but I posted a poll on Twitter and found that people generally prefer to learn testing separately, so I've devoted two modules at the end of the course to testing. That said, I know some people will want to access testing now so that they can do test-driven development throughout the course. So let's set up Mocha to run tests. As you'll see, it's not much work. Let's begin by creating a js file in the tools directory, and I'll call it testSetup.js. And I'll just paste this in from the starter kit. Now this is a lot of comments, but don't be intimidated. It's actually only about 15 lines of code. There are five core things that are going on in here. First, we're setting the NODE_ENV variable to test so that development-specific features like hot reloading are disabled when we're running tests. We're also registering Babel to transpile all of our tests that we can write our tests in ES6 too. We're disabling some webpack-specific features that Mocha doesn't understand, like requiring CSS and images. And then down here at the bottom, we're doing two things. First, we're setting up jsdom, which will provide a virtual in-memory DOM for us. This will allow us to test React components without having to open the browser. And finally, we set up a few global variables like window, document, and navigator that help simulate the browser environment. React looks for these to determine if it's in the browser, so it's important to have these available when doing DOM-based testing in React. Now that we have our tests set up, let's go update our start script in package.json so our tests are run automatically every time we start the app and hit Save.
Add Test Scripts
We're back in package.json. Let's go ahead and add a script that will run our tests via Mocha. As you can see, I'm calling Mocha and then specifying the reporter that I want to use. I prefer the progress reporter because it is compact and doesn't add a lot of noise to our terminal. And we tell Mocha to run the test setup and then run any tests that it finds in our src directory. I'm following the convention of ending all test files in test.js. Let's try running our script and see what happens. There's a shortcut. I don't have to say npm run test here. I can just say npm test, but when I do so, boom! We can see that it fails because it can't resolve the pattern that we've specified. We've specified we should be looking right up here at the src directory trying to find a test file. Since one doesn't exist, it fails. So let's go ahead and create our first test file, and we'll call it index.test.js. So, as you can see, I'm following the convention of naming tests after the file under test but with test.js on the end. Some prefer spec.js. Whatever you like for your suffix is fine. Now Mocha doesn't come with an assertion library, so we're going to use the expect library from npm. Since we don't have any logic yet, I'm simply going to stub in a test here that asserts true. You can see that I'm using the expect library and saying expect(true).toEqual(true). So this test, I'm highly confident this is going to work. Let's give it a go. And great! We have our first test passing. And if I set this to false, then we should see it fail. We'll run it again. And there it is failing as we expected. Now one final detail is missing for our test. We don't want to have to run these manually. We'd like these to run just automatically every time we save a file. So to do that, let's create another script called test:watch. And as you can see, this is just like my lint:watch up above. I'm calling the test script and then telling it that it should use the watch flag. And now that we have this, we can come up here and say that when we start the app, there's another thing we'd like to do, which is run our tests and watch our files. So now when I come down here, I should be able to say npm start -s and see our application start up in dev mode, see it fire up the browser, we see our test fail as we expected it to, and we also see our linting working. So I can come over here and fix our failing test and see that immediately apply. And our linting reruns as well the minute that I hit Save. So this is really cool. We now have a powerful development environment up and running. Let's wrap up this module with a quick summary.
Alright, we are all wired up. We've come a long way in this module. We now have a powerful and rapid feedback development environment that we'll utilize throughout the course. We're transpiling via Babel, bundling via webpack, linting via ESLint, testing via Mocha, serving the app via Express, and tying all this goodness together via npm scripts all via one command so we have one single place to check. Now we're primed and ready for building something real. But before we put all of this to use, in the next module, let's discuss the various approaches to consider when creating React components.
React Component Approaches
If you're watching this course, I assume you're already generally familiar with React, so this module isn't going to start from the beginning. I assume you already know about the virtual DOM, JSX, lifecycle methods, props, state, and React's component composition model. If you're not, refer to the React module in my React and Flux course where I start with React from the beginning. With that said, there're a few important React-specific topics that I want to discuss before we dive into Redux. In this module, I have two core goals. First, we'll discuss the many approaches that you can consider for creating React components. You'll be surprised how many ways you can create a React component these days. And we'll wrap up by contrasting container and presentational components. You'll see that each has a unique purpose. Understanding the difference will help us write clean reusable components and help assure that our application is easier to maintain and scale. Alright, let's get started by exploring the surprising number of ways that you can create a React component.
Four Ways to Create React Components
Okay, this sounds crazy, but there're currently at least four different ways to declare React components. And depending on how you count the options, there're even quite a few more. There's the ES5 style where we use createClass. This is the style that I used consistently in my React and Flux course. So if you want to see examples of that, refer back to that course. There's the ES6 class style, which we'll be using quite a bit in this course. There's the ES5 stateless function style, which we didn't use in my Flux course, but I will show here. And then finally, the ES6 stateless function style, which we'll also use quite a bit in this course. There's also many more out there. If you start looking around, there's some other interesting ways to create React components that don't require creating classes, but I'm not going to dive into those alternative methods in here. We'll use the pretty traditional options, the ES6 class and ES6 stateless function styles, here in this course.
ES5 Create Class Component
If you've used React for a while, this is likely the style that you're most familiar with. React.createClass was the original way to create React components when React was first launched, and it works great in ES5. Of course, you can still use this style today, but since we're working in ES6, we won't use this style in this course.
ES6 Class Component
ES5 Stateless Component
This is the stateless function style of component that was introduced in React.14. As you can see, it has a simpler syntax. You simply define a function, and React assumes the return statement is your render function. The only argument is the props passed in. If your component doesn't need to manage state, utilize component lifecycle methods, or do performance optimizations, you can declare a stateless functional component. Sure, the name is a mouthful, but they're actually extremely simple. Stateless functional components are exactly what their name says. They have no state, which is why they're called stateless, and they get their data solely from props, which are immutable. We'll create many stateless functional components in our app, but we're using ES6, so we'll change a few minor things about this that I'll show you in the next clip.
ES6 Stateless Component
When Should I Use Each Style?
Other Ways to Create Components
Okay, I just listed the four most popular ways to create React components. But as I alluded to earlier, there's a variety of alternative approaches to consider, including Object.create, mixins, parasitic components, and Stampit. For more information on these alternative methods, you can check this link. But I prefer sticking with the standard popular approaches that we just discussed, so in this course, we'll stick to the two ES6 styles that I just showed you. Now let's wrap up this module by discussing the two core types of React components, container and presentation components.
Container vs. Presentation Components
We just saw that there are multiple ways to create React components, but there's another decision that you have to make as well. Are you going to create a container component or a presentation component? This may be new jargon to you, so let's discuss it. Let's contrast container and presentation components. Container components are concerned with behavior, marshalling data, and actions, so these components have little or no markup. You can think of container components as the back end to the front end. Remember, components don't have to omit DOM. Container components are primarily concerned with passing data and actions down to their children. This means they're typically stateful. When working in Redux, container components are typically created using Redux's connect function at the bottom of the file, which we'll see here in a bit. Some people prefer to place container components in separate folders from their presentation components, but in this app I'm going to organize components by feature, so you'll see that our container components and presentation components will sit in the same folder. In contrast, presentation components are nearly all markup. They're just dumb. And I don't mean dumb in a bad way. I just mean that they shouldn't have logic inside. They should just be markup. Container components pass data and actions down to presentation components. Presentation components receive functions and data that they need from a container component. Container components know about Redux. They have Redux-specific code inside for dispatching actions to the store and connecting to the store via connect. Presentation components typically know nothing about Redux. This is a good thing. It makes your presentation components more reusable and easier to understand. Presentation components just rely on props to display UI. They have no dependencies on the rest of the app, such as Redux, actions, or stores. Presentation components don't specify how the data is loaded or mutated. Container components are often stateful because they need to manage state. In contrast, presentation components are typically stateless functional components because they have no need for state. This keeps their definition clean, light, and in future versions of React should also help improve performance if certain lifecycle-related logic doesn't need to run for these components. In short, try to make sure that most of your components are presentation components. And I also want to make you aware of some alternative jargon that people will use to describe this. You'll see people say container versus presentational, smart versus dumb, stateful versus stateless, controller view versus view. This latter term is what I used in the previous course on Flux, but in this course, I'm going to try to be standardized and say container and presentational. These are two terms that have become quite popular. Sometimes you'll hear me say smart and dumb components or stateful versus stateless. These are all different ways to think about the responsibility of a given component. And when trying to decide between a container component and a presentational component, I found a good quote from Dan Abramov, the creator of Redux, that I found helpful. "When you notice that some components don't use props they receive but merely forward them down…it's a good time to introduce some container components." So the point that he's making here is sometimes you may have container components at multiple layers within your application's hierarchy. It's not that we only have container components at the top of our application. You may find that in a larger application you need to introduce container components for different subsections of a given application or of a given page. Alright, that covers presentational versus container components. Now let's go ahead and wrap up this module with a short summary.
Alright, let's wrap up. In this short module, we explored the various approaches for creating React components including the ES5 createClass style, ES6 classes, ES5 stateless functions, and ES6 stateless functions. And we saw there's many more ways to consider. But in this course, we'll use ES6 classes for our container components and ES6 stateless functions for most of our presentation components. We wrapped up by discussing the uses of container and presentation components and alternative terms like smart and dumb. I'll do my best to use the terms container and presentation throughout this course for consistency. We saw that container components are the components that will typically connect to our Redux store and then pass that data down to our child components. So what's next up? Well, it's time to dive back into the editor and use the environment that we've built so that we can start coding some React components. In this next module, we're going to build out our application's initial structure.
Initial App Structure
Enough concepts. It's time to get back into code. In this module, we'll fire up the editor and create our first few React components that will form our app's foundation. To create the foundation of our app, in this module we'll create our first React components. We'll create our application's first pages. We'll create a layout that's utilized on all those pages. We'll use React Router to configure routing and set up navigation as well. So you can think of this module as a fast-paced review of React and React Router that also serves to create the foundation that we need so that we have a solid app we can enhance with Redux. Alright, let's get started.
Create Initial Components
In this course, I'm assuming that you're already generally familiar with React, so I'm going to paste in most of the React components that we use in the next few clips. Then I'll explain how each one of these works at a high level. Now it's a common convention for us to keep all of our React components in a components folder, so let's go ahead and create that. We'll put it inside the src directory and call it components. Our app is going to have multiple pages, and I like to keep the components for each page in a separate folder, so let's initially create two subfolders, home and about. So I'll create home and about. Inside each of our folders, we can create our components. I'll create the about component here and call it AboutPage.js. And I like to use an initial cap on my React components. It's not absolutely required, but a pretty popular convention. And I'll also create HomePage.js here in home page. And I'll go ahead and paste in our HomePage component, and then we can talk about it here. You can see I'm of course importing React, and then something that might be new to you is I'm importing the Link component from React Router. So React Router will be handling this anchor tag here, which will take us over to the AboutPage. The rest of this is really just boilerplate HTML. This is a class that comes from Bootstrap that will make this a rather large prominent message on our home page. And you'll see that by convention on all of my components I export the component as default down here at the bottom of our component. Now if you're not yet familiar with default exports, what I'm saying here is when someone else imports this file, they will say import HomePage from HomePage, and they will get a reference to the HomePage class. And that's possible because I exported HomePage as the default here on line 16. Okay, let's go populate our AboutPage now as well. I'll paste this in, and then we can talk about it. I'll wrap this around. Well, instead of wrapping it, I'll just do this so we can see the whole thing. What we can see is again I'm importing React, and I'm creating a class-based component here, again exporting default, just a little bit of markup. Nothing real interesting here. One thing that I should mention though is that I'm choosing to use class here rather than a stateless component. You could totally use a stateless component here, but because of some current limitations in hot reloading, it's useful to have a class somewhere at the top of your component structure because there's a limitation in hot reloading right now where stateless functional components aren't hot reloaded unless they have some parent that is a class. So by doing this, we will have hot reloading on this page. So if you don't care about hot reloading, feel free to make this a stateless functional component instead. And again, like I'd said earlier, hot reloading is a nice thing to have, but it is something that's likely to improve and change in interesting ways in the future. I included it in the course because it really is useful even today given its quirks and limitations. So great, we have a couple of pages ready. In the next clip, we'll create a parent component that will house our application's layout.
Create App Layout
We'll want a parent component that houses any markup that we want to display on every page, such as a header or a footer. Now I typically call this component App.js, but you could call it template or layout if you prefer. The point is we need a component that will load on every page and end up wrapping the components that we just created. So let's create App.js in the root of the components folder. So I'll say New, File, App.js. And then let's paste this in and take a look. There's not too much new and interesting here. What we do have that should catch your eye is here on line 9. We're just passing down the children that it receives as props. Now these children will be passed in from React Router, which we will wire up in a moment. So React Router will be passing child components as properties onto our App component, and then they will be composed right here on the page. And I've put in a little placeholder. We could imagine having a header right here in this paragraph. I'm also just using a class from Bootstrap, put this in a fluid container. Of course, I should have PropType validations since I'm expecting to receive child components. I've added children as a required PropType on this component. Now that we've created our App.js, it's time to shift over to our routing file and configure the routes for our application.
Now that we have a couple of pages ready, let's set up routing so that we can navigate between our pages using React Router. As you'll see, it's quite easy to set up. It's already installed as part of our starter kit. So let's create routes.js in the root of our src file. We'll place all of our routes here. To make this happen, we're going to need to import a few different things. I'm going to paste in our initial routes, and then let's talk this through. You can see, of course, that I need React, and then I'm pulling in Route and IndexRoute from React Router. IndexRoute is what we will use when there is just a root path that we want to expose. So IndexRoute here will reference our HomePage, which is effectively saying if somebody just goes to /, we will load the HomePage. Otherwise, if the path is /about, then we'll end up loading our AboutPage instead. You can see that I've imported our components up here above, and then I've wrapped all of this in the Route component which comes with React Router. And, of course, I've also reference our App component, which will always be loaded. By placing it here at the top, we're saying always load the App component, and then nest these other items. Pass them as children based on our routing. So if we have a URL that is just /HomePage, then HomePage will be passed as a child to our App component and will end up composed in App.js. So for another example, if I go to /about, then App.js will end up getting our AboutPage component right here because it will be passed in as a child by React Router. Now that we've declared our routes, there's one final piece to set up to get React Router going, and that's to go to our application's entry point and set it up to use React Router. So let's do that in the next clip.
Update Entry Point
In the previous clip, you saw I was referencing styles.css from our application entry point using an import statement that webpack will parse. But we haven't created styles.css yet, so let's add that. We'll go to our src directory and add a new directory called styles, and then inside I'll create styles.css. And I'll just paste in a little bit of style here. There's not much that we need because we're mostly relying upon Bootstrap's CSS. I'm just augmenting it with a little bit of my own CSS here to improve our layout. And with this all set up now, we should be able to run our application and see how routing is working. So let's give it a shot.
Great! We should have everything wired up that we can now try out our configuration using React Router and our first React components, so let's give it a shot. I'll open up the terminal and say npm start, and we get our message. See it loading up. And here we come over into the browser. Excellent! So it does load up for us now. Obviously, we can take this message out, so I'll go do that in a moment. We can see our header would end up sitting here, so we know that's getting handled properly by App.js. We have our jumbotron sitting here as expected and then our Learn more link that links to /About. So let's see if that works. Excellent! That does work, and we have nice, clean URLs here. Of course, the thing that's missing is now that I'm on the About page I'm stranded because we don't have a header. I don't have any way to navigate throughout our application. So the next thing that we need to do, we can take this off the top because that was just there as part of our starter kit and no longer needs to be hardcoded into index.html, and then we can go ahead and create our new header component. So let's come back over here, open index.html, and we'll just take out this h1 tag and hit Save. And now that I've taken that out and saved, let's come over here and reload. There we go. That took it out of the top. And by the way, I had to reload there because not everything is set up to hot reload. Our HTML is not hot reloading here, so this top-level HTML isn't part of our hot reloading lifecycle. It is our React components that are going to be placed in the div and then also, ultimately, our Redux stores that will be hot reloaded. So anything that ends up getting placed here will be getting that goodness as part of our new configuration. So I mentioned that we need to create a header component. Let's come over here and create it. I'm going to create a new folder. I'll call it common because header is going to be common. It's not really specific to a given page. So I like to keep common components that are utilized on multiple pages in a folder called common. And then inside here I'll create a file and call it Header.js. And here we're going to create our first stateless functional component, and we'll call it Header, of course. You can see I just create an arrow function here and have a body inside. What I'm doing is using the IndexLink component from React Router to handle this IndexLink, which just has a slash in it, and I'm also using a nice little feature that comes along with both Link and IndexLink to say when this link is active based on the route, go ahead and apply a class for me. So this allows me to style the currently selected anchor up in the header. And then in between our anchor links, I'm just putting a pipe in here. This is a really simple layout. I don't want to overcomplicate it. This should do the trick. So now we've created our Header component, but the other thing that we need to do is actually utilize it in App.js. So let's go over to App.js and take out what says Header here and instead put a reference in to our Header component. Now, of course, we need to import our Header component. And here is where the joys of hot reloading begin to shine because I haven't even switched over to the browser, but I just hit Save, and let me assure you that if we go over to the browser now, we'll see our header is there. It reloaded for us, and I'm able to navigate back to Home. Notice also that it stayed on the About page because that's where we were last time. So I can close our tags here. We can see we can now navigate to About this way, move back to Home. We have nice, clean URLs. So we now have a solid structure, a good foundation for building a more complex app in future clips. Of course, the real point of this application is to manage course data. Pretend that you work for Pluralsight. and you need to administer all the different courses that are published on Pluralsight. That's what this application will be for. So in the next clip, we'll add a course page to our application so that we can display some course information.
Create Course Page
Let's get busy creating some functionality. Since the app that we're building is for administering course data on Pluralsight, we obviously need a page where we will display our course data. So let's go into our components folder and add a directory called course. Inside of our course directory, we'll create our first course-related component, and we will call it Courses.js. Sorry, we'll call it CoursesPage.js. We'll stick with this convention of using a page suffix to signify any of our top-level components, what people would commonly call our container components in Redux terms. Let's start with basically an empty shell here. We'll just import React and put a header in that says Courses on our CoursesPage. And before we go further, let's just update routing so that we can get to this page. Obviously, we need to import our CoursesPage from ./components/course/CoursesPage. And then we'll need a new route. I'll just copy this route down, change the path to courses, and change the component to CoursesPage. So now we've added the route. The other piece that we need to handle, of course, is updating our header so that we have a link to this new component. So let's copy these two lines and just paste another instance of those. This will become our link to Courses. And now we should have the plumbing that we need to be able to navigate to our empty Courses page. Let's check it out. It looks like we have an issue. I'm still not landing at Courses. Am I receiving? Location "/courses" did not match any routes. So let's give that a look. And actually, in this case, after looking into it, I just needed to refresh was all it was. This is another one of those examples where sometimes hot reloading may not work for you, and you'll have to refresh the page. So now I'm able to navigate to the different pages. I can go to Home, Courses, and About. Great! We now have a good structure to be able to add in course management and navigate throughout our application, so we can finally put Redux to use. And that's exactly what we'll do in the next module.
In this short module, we created the foundation of our React and Redux applications. We created our first few React components, and we set up React Router so that we can navigate between pages. Now that we have a solid app to build upon, we're ready to begin exploring Redux in the next few modules.
Intro to Redux
In the first few modules, we created a solid foundation with a robust development environment and a solid application structure using React and React Router. In this module, we'll explore how Redux can help us manage our application's data flows via a quick introduction. In this module, we'll begin by asking the most obvious question. Do I need Redux? Like any tool, it's useful in certain contexts and not necessary for everyone. Then we'll consider Redux's three core principles. From there, I'll show how these three principles impact Redux's design. I'll contrast Flux with Redux in detail. This section is really useful for those who already know Flux well. It should help you get up to speed on Redux pretty quickly. Then we'll wrap up by reviewing a simple example of the full Redux flow. Alright, let's dive into Redux.
Do I Need Redux?
Three Core Redux Principles
Redux has three core principles. The first is that all your application state is placed in a single immutable store. And by immutable, I mean the state can't be changed. I'll get into how this works in a moment. But as you'll see, the single-store concept isn't just conceptually simpler than Flux's multi-store model. Having one immutable store aids debugging, supports server rendering, and makes things like undo/redo easily possible. In Redux, the only way to mutate state is to emit an action, which describes a user's intent. So, for example, a user might click the Submit Contact Form button, and that would trigger a Submit Contact Form action. The final principle is that state is changed by pure functions. These functions are called reducers. It sounds complicated, but it's not. In Redux, a reducer is just a function that accepts the current state in an action and returns a new state. And to help provide a kickstart for those that are familiar with Flux, in the next clip, I'll compare Flux and Redux.
Perhaps you're already familiar with handling unidirectional data flows via Flux. You don't have to know Flux to work with Redux, but understanding Flux will certainly help you pick up Redux more quickly. Alright, let's begin our comparison of Flux and Redux by discussing what they have in common. Flux and Redux are two different ways that you can handle state and data flows in your React applications. Both Flux and Redux have the same unidirectional flow philosophy. Data flows down. Actions flow up. So the first similarity is that both Flux and Redux enforce unidirectional data flows. All data changes flow in one direction. They also both utilize a finite set of actions that define how state can be changed. You can define action creators to generate these actions and use constants called action types in both as well. They also both have the concept of a store that holds state, though Redux has a single store while Flux allows multiple. While these core concepts exist in both, you're about to see that they differ in a variety of ways. Let's explore how Flux and Redux are different.
If you're already familiar with Flux, Redux introduces a few new concepts. Reducers are functions that take the current state in an action and then return a new state. So reducers are pure functions. Containers are just React components, but their use is specific. Container components contain the necessary logic for marshalling data and actions, which they typically pass down to dumb components via props. This clear separation helps keep most of your React components very simple, pure functions that receive data via props. This makes them easy to test and simple to reuse. The third new concept is immutability. The Redux store is immutable. So in an upcoming clip, we'll discuss approaches for working with immutable data in your reducers. Flux has three core concepts, actions, dispatchers, and stores. When actions are triggered, stores are notified by the dispatcher, so Flux uses a singleton dispatcher to connect actions to stores. Stores use EventEmitter to connect to the dispatcher. So in Flux, each store that wants to know about actions needs to explicitly connect itself to the dispatcher, typically by using EventEmitter. In contrast, Redux doesn't have a dispatcher at all. Redux relies on pure functions called reducers, so it doesn't need a dispatcher. Pure functions are easy to compose, so no dispatcher is necessary. Each action is ultimately handled by one or more reducers which update the single store. Since state is immutable in Redux, the reducer returns a new updated copy of state, which updates the store. Let's contrast Flux and Redux further by exploring the specific ways that they differ. In Flux, stores do more than one thing. They don't just contain application state. They also contain the logic for changing state. Redux honors the single responsibility principle by separating the logic for handling state. Redux handles all state changing logic with reducers. Reducers are quite simple. A reducer specifies how state should change for a given action. So a reducer is a function that accepts the current state and returns an action. Flux supports having multiple stores. So in a Flux application, you may have a user store and a product store. You can have as many stores as you like. In Redux, you only have one store. This sounds constraining, but as you'll see, there are a number of significant advantages to the simplicity of having a single store. Having a single source of truth helps avoid storing the same data in multiple places, and it also avoids the complexity of handling interactions between stores. One common struggle in Flux is how to deal with stores that interact with one another. In Flux, the stores are disconnected, though Flux does at least provide a way for stores to interact via the waitFor function. In contrast, Redux's single store model avoids the complexity of handling interactions between multiple stores. This is conceptually simpler, and it provides some unique advantages that we'll discuss further in upcoming slides. In order to handle more complex stores with many potential actions, you can utilize multiple reducers, and you can even nest them. See, in Flux, stores are flat, but in Redux, reducers can be nested via functional composition, just like React components can be nested. This is a nice symmetry. So Redux gives you the same power of composition and nesting in your reducers as you have today in React's component model. In Flux, a dispatcher sits at the center of your application. The dispatcher connects your actions to your stores. In Redux, there's no dispatcher because Redux's single store just passes actions down to the reducers that you define. It does so by calling a root reducer that you define yourself. Reducers are pure functions, so in Redux, there's no need for Flux's EventEmitter pattern. See, in Flux, you have to explicitly subscribe your React views to your stores using onChange handlers and EventEmitter. In Redux, this can be handled for you using react-redux. React-redux is a companion library that connects your React components to the Redux store automatically. We'll use this library in the course. React-redux contains a connect method which generates a top-level React component that's connected to your actions and store. Every time the store's state changes, it calls a function that triggers a rerender on your component. Finally, in Flux, you manipulate state directly. It's mutable. In Redux, state is immutable, so you need to return an updated copy of state rather than manipulating it directly. You'll see how to do this in an upcoming clip when we talk about reducers. So those are the major differences in a nutshell. Now let's explore Redux at a high level by reviewing a simple example of data flows.
Redux Flow Overview
Now that we've contrasted Flux and Redux, let's explore Redux in more detail. Let's look at how actions, reducers, the store, and container components will interact to create unidirectional data flows. Let's consider a simple example to understand this flow. An action describes user intent. It's an object with a type property and some data. The data portion can be whatever shape you like. The only requirement is that an action has a type. If you've already worked with Flux, then this should look familiar. This concept doesn't change in Redux. This is an action for rating a course. Imagine you were rating my course on a scale of 1 to 5. Let's say you rated it at a 5. Come on, be my friend! You know you want to. Okay, anyway, if you did click to rate it a 5-star course, this is the action that would be produced. This data portion on the right can be whatever you like. You could pass multiple separate pieces of data here or one or more objects. This action will ultimately be handled by a reducer. A reducer is just a fancy name for a function that returns new state. So as you can see, the reducer receives the current state and an action, and then it returns a new state. Reducers typically contain a switch statement that checks the type of the action passed. This determines what new state should be returned. And once this new state is returned from a reducer, the store is updated. React re-renders any components that are utilizing the data. Your React components are connected to the store using react-redux. We'll get to how that works in an upcoming module. But that's the Redux flow in a nutshell. Not too bad!
In this short module, we took a quick look at Redux at the high level. We discussed the various scenarios where Redux is useful, which basically summed up to if you need it, you'll know it. I covered the three core design principles of Redux, that state is immutable, actions trigger changes, and reducers return updated state. We saw how Flux and Redux are similar but differ in key ways, particularly around Redux's lack of a dispatcher, immutable single store, and its store subscription approach. We wrapped up by reviewing a simple example of unidirectional flow. And now that you're generally familiar with actions, stores, and reducers, in the next module, we'll explore these core concepts in more detail.
Actions, Stores, and Reducers
Just like in Flux, in Redux, the events happening in the application are called actions. Actions are just plain objects containing a description of an event. So here's the plain object. This is the action. An action must have a type property. The rest of its shape is up to you. Here I'm passing some data under a property called rating. This could be a complex object, a simple number, a Boolean, any value that's serializable. The only things that you shouldn't try passing around in your actions are things that won't serialize to JSON, like functions or promises. Actions are made by convenience functions called action creators. Here, the action creator is called RATE_COURSE. Typically, the action creator has the same name as the action's type. Action creators are considered convenience functions because they're not required, but I recommend following this simple convention. By using these action creators to create your actions, the spot where you dispatch the action does not need to know the action creator's structure. The app we're creating will work with course data, so it will have actions like LOAD_COURSE, CREATE_COURSE, and DELETE_COURSE. Now when actions are dispatched, it ultimately affects what data is in the store, so let's discuss the store next.
In Redux, you create a store by calling createStore in your application's entry point. You pass the createStore function to your reducer function. This is a point of contrast with Flux because in Flux the store mixes concerns. Flux stores contain both the data and the logic for manipulating the data. The Redux store honors the single responsibility principle because the store simply stores data while reducers, which we'll discuss in a moment, handle the state changes. You might be concerned that there's only one store in Redux, but this is a key feature. Having a single source of truth makes the application easier to manage and understand. The Redux store API is very simple. The store can dispatch an action, subscribe to a listener, return its current state, and replace a reducer. This last feature is useful to support hot reloading. The most interesting omission here is that there's no API for changing data in the store. That's a good thing. It means the only way that you can change the store is by dispatching an action. That's why I'm showing the padlock icon on the store. You can't change it directly. The store doesn't actually handle the actions that you dispatch. As you'll see in a moment, actions are ultimately handled by reducers. To understand reducers, we need to discuss immutability first, so let's do that in the next few clips.
What Is Immutability?
We now have a good foundational understanding of immutability, so let's discuss how data changes are handled with reducers. To change the store, you dispatch an action that is ultimately handled by a reducer. A reducer is quite simple. It's a function that takes state and an action and returns new state. That's it. You can think of a reducer like a meat grinder. With a meat grinder, you put in some ingredients and turn the handle, and then the results come out the other side. In the same way, with reducers, you pass in some ingredients, in this case, the current state and an action, and it spits out a new state. Don't like the meat grinder metaphor? Okay, let's try one that's more cuddly. Reducers sound scary, but they're actually like a fluffy bunny. They're so approachable. They're so simple and so tasty. Wait, I don't eat rabbits. I swear. Never mind. Here's an example of a reducer that's handling incrementing a counter. Reducer functions just look at the action passed and return a new copy of state. So, for example, if the action passed was INCREMENT_COUNTER, then it would increment the counter and return the new state. The reducer knew what state needed to be changed by looking at the action passed, and it updated state accordingly. However, there's one thing wrong with my example. I'm mutating state right here. As we've discussed, in Redux, state is immutable. So, in other words, it cannot be changed. So let's update this example to return a new copy of state instead. Here's an updated example. This example doesn't mutate state. I'm using Object.assign to create a new copy of state. Let's dissect this line. Here I'm saying create a new empty object. The first parameter is the target, so we're just creating a new empty object. But then we're mixing that new object together with our existing state and also changing the counter property by incrementing it by 1. So the result is effectively a deep clone of our state object, but with the counter incremented by 1. Remember, reducers must be pure functions. This means they should produce no side effects. You know you have a pure function if calling it with the same set of arguments always returns the same value. Because reducers are supposed to be pure functions, there are three things that you should never do in a reducer, mutate arguments, perform side effects like API calls and routing transitions, or call non-pure functions. A reducer's return value should depend solely on the values of its parameters. It shouldn't call non-pure functions like date.now or math.random. This way, the reducer stays pure. It simply takes the current state and an action and returns the new state. No mutations or side effects, just a pure predictable result. I mentioned earlier that you can only have one store in Redux. The original Flux pattern describes having multiple stores in an app, each one holding a different area of domain data. That sounded good, but it has downsides such as needing one store to wait for another store to update. This isn't necessary in Redux because the separation between data domains is already achieved by splitting a single reducer into multiple smaller reducers. So while you might think having one store would be limiting and lead to huge monolithic stores that are hard to manage, in practice, it's not a problem because you can manage slices of your state changes via multiple reducers. That said, it's technically possible to create multiple stores when working in Redux, but it's not recommended and only useful in rare instances. In short, assume you can only have one store when working in Redux. Only try creating another after very carefully investigating the implications. When the store is created, Redux calls the reducers and uses their return values as initial state. But you might be wondering if we have multiple reducers, which one is called when an action is dispatched? The answer, all of them. All reducers get called when an action is dispatched. The switch statement inside each reducer looks at the action type to determine if it has anything to do. That's why it's important that all reducers return untouched state as the default if no switch case matched the action type passed. So, for example, if I dispatched the DELETE_COURSE action and my app has three reducers, one for courses, one for authors, and one that handles loading status, all three of these reducers will be called. But only the reducers that actually handle the DELETE_COURSE action type will do anything. The others will simply return the state that was passed to them. Remember, each reducer only handles its slice of state. In fact, each reducer is only passed its slice of state so that it can only access the portion of state that it manages. So while there's only a single store for Redux, creating multiple reducers allows you to handle changes to different pieces of the store in isolation. This makes state changes easy to understand and avoids issues with side effects. Just remember, all the reducers together form the complete picture of what's in your store. I like to think of my store like a pie chart, and all of my reducers are handling a piece of the pie. One final note on reducers before we wrap this up. You might wonder if there's always a 1:1 mapping between reducers and actions. Nope. In fact, the Redux FAQ recommends using reducer composition. This means a given action can be handled by more than one reducer. We'll see an example of this in a later module when building our example app. As the FAQ says, "Write independent small reducer functions that are each responsible for updates to a specific slice of state. We call this pattern "reducer composition". A given action could be handled by all, some, or none of them."
Connecting React to Redux
We've explored nearly all of the Redux API that we're going to use in detail at this point. The one obvious unanswered question is how do I connect my React components to Redux? The great news is Redux pulls this off in a really elegant way. So in this module, we'll begin by quickly reviewing the difference between container and presentation components. Then we'll check out react-redux, the Redux companion library that will connect our React components to our Redux store. You'll see how the Provider component wraps the application so that the Redux store is available, and you'll see how to use the connect function to connect React components to the store and specify what properties and actions you'd like to attach to your component. And we'll wrap up our discussion with a summary style that's inspired by the great Kathy Sierra that I like to call A Chat with Redux. The rest of the course is almost exclusively writing code, so let's quickly wrap up these final key concepts.
Container vs. Presentational Components
In a previous module, we talked about container and presentational components, also known as smart and dumb components. To understand react-redux, it's important to understand two types of React components. Redux's documentation uses the terms container and presentational components, and I sometimes call these smart and dumb components since containers contain all the smarts necessary to support the dumb presentational components below. I want to cover some information that's largely recreated from the Redux documentation here. This is absolutely key information for understanding how best to work with Redux and React. Container components are focused on how things work. They handle data and state so that all the dumb child components below can simply receive the data and actions that they need via props. That's why they're called presentational components. Container components are the only components in your system that are aware of Redux at all. This is a great thing because it means your child components are dumb presentational components that simply receive data and actions via props and contain markup. This ensures that your presentational components are easy to understand and can be easily reused since they have no dependencies. Container and presentational components also differ in how they get their data. Container components subscribe to Redux state, while presentational components read data from props. In a similar way, container components actually dispatch Redux actions, while dumb presentational components fire off actions by invoking the callbacks passed down to them via props. So in this way, a presentational component isn't tied to a specific behavior. Its behavior is passed down from a container component via props. Finally, container components aren't typically written by hand. As you're about to see, they're generated via react-redux. You could certainly write container components by hand since a container component is just a React component that uses store.subscribe to read a part of the Redux state tree and supply props to child components, but react-redux does many complex performance optimizations for you, so you'll want to use it to create your container components. In contrast, presentational components are written completely by hand. They're typically stateless functional components. Since they merely receive data and actions via props, all they often need is a render function to define their markup. Alright, with this difference established, let's discuss how to connect our React components to Redux.
React-redux's connect function accepts two parameters, both of which are functions, and both of these parameters are optional. The first parameter is mapStateToProps. This function is useful for defining what part of the Redux store you want to expose on your component. When you define this function, the component will subscribe to Redux store updates. Any time it updates, mapStateToProps will be called. This function returns an object. Each property on the object you define will become a property on your container component. So in summary, the mapStateToProps function determines what state is available on your container component via props. This is a logical place to filter or, otherwise, transform your state so that it's most conveniently shaped and sorted for your component's use. Okay, that sounded confusing, but it's really not that complicated. Let's look at a simple example. If you're building a simple app, you may have only one reducer and one container component. In that case, you'd just want to pass down all of your state. But as your application grows, you'll likely want to create multiple container components to manage different pages or sections of your app. You'll likely want to create different reducers to handle different slices of your store as well. This is an example of a simple mapStateToProps function that simply makes all of your state accessible to the component via props. So with this setup, I could say this.props.appstate within the component to access any state that is handled by my appstate reducer. What if I only want to expose part of my store's state to the component? Well, then I can specify the specific pieces of state that I want to expose via props right here. Each object will become a prop on my component. I'll show more examples as we jump back in the code in the next module. One important thing to note is every time the component is updated, the mapStateToProps function is called. So if you're doing something expensive in there, you'll want to use a library like Reselect for memoizing. Memoizing is about keeping track of the results of each function call so that the function doesn't have to run again if it's already been run with the same parameters. So memoization is like caching for function calls. Each time a function is called, Reselect just checks whether it's already been called with the specified parameters, and if it has, it doesn't call the function. Instead, it just returns the memoized value instead. This is useful for increasing performance by avoiding unnecessary expensive operations. In summary, if you're doing expensive operations and you're mapping, for instance, filtering a list or making expensive calculations, then memoization can ensure that these expensive operations only occur when actually necessary. So if you're doing expensive work in mapStateToProps, consider adding the Reselect library.
The second function that we pass to connect is mapDispatchToProps. This function lets us specify what actions we want to expose as props. So this is conceptually very similar to mapStateToProps. The difference is this function determines what actions we want to expose to our component instead of what state. The mapDispatchToProps function receives dispatch as its lone parameter. It returns the callback props that you want to pass down. The bindActionCreators function that you see here is part of Redux. To clarify its use, let's step back and consider the three different ways of passing actions down to components using Redux. As we've already seen, Redux is lightweight and not overly opinionated. So there are three ways to handle mapping your actions to props in Redux container components. The first option is to ignore it since mapDispatchToProps is an optional parameter on the connect function. When you omit it, then the dispatch function will be attached to your container component. This means you can call dispatch manually and pass it an action creator. We'll explore this in more detail in a moment. The second option is to manually wrap your action creators in dispatch calls within the mapDispatchToProps function. Here I'm wrapping my loadCourses action creator in a function that calls dispatch. Compared to option 1, it keeps the calls in my actual component shorter at the cost of some extra coding here in mapDispatchToProps. Or, finally, you can use the bindActionCreators function, which is a convenience function that wraps your action creators in dispatch calls for you. BindActionCreators basically does what we're doing in option 2 automatically. Now this all sounds pretty obscure at first. But remember, this is simply a decision about how you want to expose your actions to your components. Let's look at examples of each of these approaches in more detail to help clear things up. As I just mentioned, one simple option is to ignore the mapDispatchToProps function altogether. Calling connect on your component automatically adds a dispatch prop to your component. You can use this dispatch prop to call your action creators, as I'm doing here. However, there's a couple of downsides with this approach. First, it requires more boilerplate each time you want to fire off an action because you have to explicitly call dispatch and pass it the action you'd like to fire. Second, this means your child components need to reference Redux-specific concepts, like the dispatch function, as well as your action creators. If you want to keep your child components as simple as possible and avoid tying them to Redux, then this approach is not ideal. The second option is to manually wrap your action creators in dispatch calls. Here I'm specifying the actions I want to expose to my component explicitly. One by one, I wrap each action creator in a dispatch call, and then this is how my call would look within the component. When you're getting started, I recommend using this option because manually wrapping action creators makes it clear what you're doing. But as you can see, it's quite redundant. That's why you may prefer to use option number 3, which is bindActionCreators. This function ships with Redux to handle this redundancy for you. With this approach, the bindActionCreators function will wrap all the actions passed to it in a dispatch call for you. Of course, the props created by these two examples is slightly different. Notice that the prop that will be exposed to the component here is called actions. But if we go back to the previous slide, we are exposing this.props.loadCourses, this.props.createCourse, and so on. So it's a minor difference in the way that I chose to wire this up. The bottom line is approach 2 and 3 both produce the same result. They wrap your actions in a dispatch call so that they're easy to pass down to child components. But there's a notable advantage to option 2 and 3 over option 1, and that is that with options 2 and 3 your child components don't have to know anything about Redux. Child components can simply call the actions that are passed down to them via props. Remember, with option 1, we had to import action creators into our child components so that we could call Redux's dispatch directly. I know this is confusing without an example that's in proper context, so don't worry. In the next module, we'll use all three of these approaches. And before we close out this module, let's have a quick chat with Redux to wrap up everything that we've learned.
A Chat with Redux
We just went over all the core players in a Redux app, actions, reducers, the store, react-redux, and React container views. That's a lot of new concepts, so it's easy to get confused at first. I find it helpful to think about the major players as people with different roles who interact with each other. Here's an example conversation that I played through my head. React says hey, CourseAction, someone clicked this "Save Course" button. Well thanks, React! I'll update an action so reducers that care can update state. And the reducer says ah, thanks action. I see you passed me the current state and the action to perform. I'll make a new copy of state and return it. The store says thank you for updating the state, Mr. Reducer. I'll make sure that all connected components are aware. React Redux says whoa, thanks for the new data, Mr. Store. I'll now intelligently determine if I should tell React about this change so that it only has to bother with updating the UI if it's necessary. And React says ooo! Shiny new data has been passed down via props from the store! I'll update the UI to reflect this. And that's how data flows through Redux in a unidirectional manner. Alright, let's wrap up this module with a quick summary.
Alright, we've spent the last few modules building a strong foundation. Now we have all the pieces in place to finally dive into code for the rest of the course. There are very few slides from here on out. We already have the knowledge we need, so let's put it to use and start coding. In this module, we'll build our first feature using Redux. We'll create a simple form for adding courses. We'll define actions and action creators and set up our Redux store. We'll handle state changes via a reducer and ultimately complete the loop by wiring up our first container component, which will connect to our Redux store using Redux connect. In this single module, we'll experience the flow of working in Redux from the ground up by building our first feature. Alright, fire up your editor of choice. Let's get coding!
Create Simple Add Course Form
For our first use of Redux, let's create a new course. To do that, we're going to build our first container component. We're going to need a form to input a course. Normally, we'd want to create a separate component that would house our markup, but for simplicity, let's just add our form directly here on the CoursesPage component. For our first step, let's add a constructor so we can initialize state for the form in the constructor. Now to keep this example simple, we'll assume that a course simply has a title for now, and I'll just paste in the constructor up here. So we can see we are setting some local state, and we are going to have a course, and title is the only property that it will have. So courses are very simple at this point. And next up, let's place our form on the page. I'm going to paste that in right here below the h1. And what we can see here, I've got one extra div I can remove, is we have an h1 for Courses, an h2 for Add Course, and then an input and another input, one input for submitting our form and another one that will take our course's title. Now you can see we are calling out to a function that doesn't exist yet, well, actually two functions that don't exist yet because we don't have onClickSave or onTitleChange yet. So the next step is to create our onTitleChange function. To do that, let's move up here above render and add in onTitleChange. What we can see is that this does something pretty standard. We pull in the event, and we will pull the value out of that event off of the target, and then we will set that to the title and then update our state by calling setState. So this will effectively update our state every time that somebody presses a key when their focused on our title input field. So this creates our initial form structure. In the next clip, we'll add our onClickSave function and see how to use bind when working in ES6 classes.
Binding in ES6
Now that we have our basic form structure, we also need to handle the onClickSave function, so let's add that in right here above the render. I'm not going to do anything special here. I'll just alert out the message that we receive so that we can see that we are getting the title updated right here from our local state. And now that we have this ready, we should be able to run the application, so let's open the terminal and type npm start, and let's switch over to the browser now and go to our Courses page. So we can see our form is displaying just fine, and I can type in the input, which is good. But when I hit Save, I don't get the alert that we're expecting, so let's inspect and see if we're receiving any errors. And it looks like we have two issues here. The first is a warning that occurs on newer versions of React. We need to populate the value of input with something other than null. So, instead, let's go ahead and populate it with an empty string, and that should resolve that warning. Come back over here, and we can see that warning's resolved. But now every time that I hit a keystroke in this input, you can see that we're getting Cannot read property 'state' of undefined. Well that's odd because state should be defined, but the problem is that state isn't defined here because of the this context that's getting passed. If you've only worked in React with ES5, you're likely confused about why state is undefined in this case. See, when you use React's createClass function, then functions are autobound for you, so you didn't have to use bind in instances like this. However, React doesn't autobind in ES6 classes, so we have to handle binding ourselves. The issue in this case is that the this context is currently wrong in our change handler. Our function is inheriting the this context of the caller, which in this case is the change handler. Let's go over here, and let me show what I mean, that every time that I am having a change event occur down here on my input, it is passing the this context of this input over to our change handler for onTitleChange, and that's why this is not the this that we're expecting. It needs to be bound to the instance of our component. So to fix this, let's bind to the this context up in our constructor. I'm going to add in bind statements for both of our functions, the onTitleChange function and the onClickSave function. All I'm doing here is binding them to the this of our CoursesPage component. And this will solve our problem. Now there's another way that you could get this done, which is doing the bind down here within render. I could say this.onTitleChange.bind(this). Now this approach does work as well, and, in fact, you'll see many people do this in example code. There is one downside to this, which is performance. Every time that you do a bind in render, you're impacting performance because using bind in render causes a new function to be created on each render. So this style really is a performance issue. For performance, we should avoid defining new functions in our render function. Instead, it's best to place your bind calls up here in the constructor. So I'll take this style back out. Let's hit Save and see whether we're working now. Now it looks like we're getting Cannot read property 'state' of null. Let's try refreshing, and I'll enter a few keys and hit Save. There we go. So this is one of those great examples of where hot reloading didn't do us any favors. We were getting an odd error. And from my experience, this just sometimes happens with hot reloading. That's the caveat with it. But we can see now that we do have this wired up properly, and it is displaying the title as we would expect. So in summary, just be sure throughout this course to do your binds in the constructor rather than down in the render. It does work both ways, but this is the recommended pattern and what we will use throughout this course. Now that we have our initial form working, it's time to shift our focus over to creating our first action for Redux.
We now have a form that's all set up and ready to send data, so it's time to wire it up to Redux. To begin setting up our Redux flow, let's create a few folders where we'll keep Redux-related files. These first few steps will feel very familiar to you if you've ever worked in Flux. First, let's create a folder for actions right here under src, and I'll call it actions. Inside this folder, let's create courseActions.js. This file is going to hold our course-related action creators, and since we're creating a course, let's call our first action creator createCourse. And action creators, we will just export a function called createCourse that accepts a course. And then inside of here is where we define our action. Our action is an object that has a property of type. This type property is required. CREATE_COURSE. I will just hard code in the string here, although we will move this out to a constant later. I'll show how to do that. But for simplicity, we'll do things like this. Now, just like in Flux, our action creator returns a plain object that must have a type property that you see here. This is required. And remember, this function is called an action creator because that's just what it does. It creates actions. The type property specifies the action's type, and for now, I'm hardcoding in CREATE_COURSE. But in a moment, we'll use a constant. Now just like in Flux, this is a convenience function, and what it returns is an action. The only requirement of an action is that it has a type property. The rest of its shape can be whatever works best for you. For this action, I'm just passing course data, but over here, I could have as many properties as I want. Now you'll notice that I'm simply saying course here because in ES6 we can omit the right-hand side if it matches the left-hand side. So I can say this if that's clearer to you, but in ES6 I don't have to. Because the right-hand side matches the left-hand side, I can just omit it. It's a little bit more terse. This is our action creator. In the next clip, we will create our first reducer.
Now that we've created our action, we need a function that will handle that action, and that's where reducers come in. If you've worked in Flux, then this is where you'll notice a shift. See, in Flux, you'd handle actions with your store, but in Redux, you handle actions within reducers. Reducers sounded complicated to me at first, but I now realize they're actually quite simple. A reducer is just a function that accepts a state and an action and then returns a new state. So let's create our first reducer. First, we'll go to the src directory and create another new directory and call it reducers. Inside this folder, let's create a new file, and we'll call it courseReducer.js. As far as naming goes, some people like to put the suffix Reducer on the end of their file. Other people leave that off. I could just call this course.js or courses.js since it's working with course-related data. It's really a personal preference. I like to put the suffix Reducer on here because then my tab up top is clearly my reducer. If I called this course or courses, it might end up confusing me whether that tab is for the component or for the reducer, so I find this suffix helpful. Alright, let's begin creating our first reducer. I'm going to export default a function, and we will call our function courseReducer. Again, this is another place where people shift a bit. You don't have to give this a name at all. I could just omit the name, but we'll go ahead and call it courseReducer. I find that helpful. And the reducer is going to take two parameters. The first is state, and the second is action because, again, what a reducer does is takes the current state and an action and then returns a new state, so it's really quite simple. Now, to set our initial state, we can use the default parameters feature that's part of ES6. I'm going to set the default state here to an array because this reducer's going to handle our list of courses, and by setting an empty array here, I'm saying that we're starting out with no course. Let's open our function. And there's a few different ways that you could structure the inside of your reducer. I'm going to use the most common approach, which is a switch statement, and this switch statement's going to allow me to fork my logic based on the action that's coming in. We will look at the action's type that's passed. Remember, this is exactly why type is required on our actions. The only case that we're going to have to handle so far is CREATE_COURSE. Remember the type that we hardcoded in on the other side. And here, the question is what do we do? Well one simple thing we can do here, I'll say state.push the action's course, and then, excuse me, here on the next line, I'll go ahead and return that state. Now if you paid attention on the slides earlier, this should look a little bit wrong to you because remember we talked about how in Redux state is immutable. So I shouldn't be mutating state here, but I'm showing you this simple setup just to show what might seem like the most obvious thing to do at first. I'll leave this here just for the moment, and then we'll discuss this further. Of course, any time you create a switch statement, it's a good idea to have a default. And that's especially true here because we could have multiple reducers that are handling different actions. And if this reducer doesn't happen to handle this action, then what we should do is just return state. So this is the basic structure of our reducer. Now what I showed here I mentioned we can't do because state is immutable, so let me show you a different way to handle this that would honor our immutable state. Instead, let's use the ES6 spread operator to get this done. So I will return a new array, use the spread operator here on our existing state, and then use Object.assign and pass it our target object, which is an empty object in this case and then the course that's passed on our action. I will close out the array, and now we're set. Now that probably looks really strange to you if you haven't worked in ES6 before, so let's talk this through. What the ES6 spread operator does is spread the array. So effectively, what we see here is representing our existing array and then exploding it out as though I had taken all the values in it and defined them here inline. So this ends up returning a new instance of our state array. Then I can use Object.assign to create a deep copy of the course that's passed in. This way, these two values together end up returning a brand-new state that contains the new course that someone has just passed in via the action. I know it's a bit of a mouthful, but once you get comfortable with the spread operator and Object.assign, what you'll find is these are two tools that are used heavily in ES6 and used often within reducers because they're such a handy way to be able to copy over an array and create a new array with an extra value inside. Okay, one other note before I wrap up here. One thing you might also be thinking is ooh, yuck, a big switch? That's a common concern about Flux and Redux, that actions are typically handled via switch statement. This doesn't personally bother me, but if you don't like the switch statement, understand that there are alternative approaches to consider, though switch is the most common. But it's fine to use if statements, a lookup table of functions, or even create a function that completely abstracts this away. The point is each reducer handles a specific slice of state. So even though in Redux you have just one store, reducers let you slice up the management of your store's state changes into a number of separate functions. And that's exactly why this switch is useful, so that we can say for these specific action types I want to perform some functions. And for any that I don't define up here above, I just want to return the existing state. Now most applications will end up having multiple reducers inside of them. Right now we only have one, but we will end up creating others as the course progresses. So now's a good time for us to create our rootReducer.
Okay, we only have one reducer so far, but as I mentioned, Redux supports multiple reducers, and most apps will end up using more than one reducer. So for now, let's create our rootReducer just so we have it set up. We'll go over here to our reducer's folder and create a new file. And traditionally, the rootReducer is called index.js. Inside, we're going to reference a function that comes from Redux called combineReducers, and we're also going to need to import our courseReducer that we just created. Now we can define our rootReducer right here, and we'll use the combineReducers function that we just imported. Inside of here, we define all of the reducers that we're wanting to combine for our application. Of course, we only have one reducer right now called courses, so I'll place it here. Couple of things that we should notice. The first is that I call my course reducer courseReducer. I could've called it course or courses, but I chose to call it courseReducer just so that it would be clear up here in my tab structure that I'm clearly looking at a reducer file. But you can see, since it has exported default, I can go ahead and alias it however I want. I'm calling it courses here. And you'll find that this is important because on my rootReducer, the name that I supply right here, or I should say the property that I supply here, will impact that way that I access this state throughout my application. So in my container components, I'll be saying state.courses here. If I had called this instead courseReducer, then I would have to say state.courseReducer, which doesn't read as well. So I recommend thinking carefully about the property name that you choose here. And again, we're using shorthand properties because what we're defining here is an object, but I could do this instead. I'm defining an object that maps courses to courses in this case. So I don't need this right-hand side. I can just say courses, and it's a little bit more terse. Of course, the final piece that I'm missing is exporting from our file the rootReducer. Now, admittedly, we didn't need this code right now, but as soon as we wanted to add a second reducer, then we would need to create our rootReducer, so I just decided it was a good time to go ahead and take care of this. And one thing I should've stated slightly differently here, this is called the shorthand property name. So if you want to Google on this, we are using ES6 shorthand property names in this case. Great! So we've created our first reducer and our rootReducer. In the next clip, let's shift our focus to creating our Redux store.
Next up, let's create our Redux store. In Redux, there's a single store, so we'll create a folder called store right here in our src directory. Inside, let's create a file called configureStore.js. Now we're ready to configure our Redux store. Now when creating a store, it's useful to define a function that configures the store because we'll call this function at our application's entry point. This way, the store is configured when the app starts up. Let's begin by importing a few functions that we need though, which is the createStore function from Redux, and we'll also need a reference to our rootReducer, which we just created. As I mentioned, we're going to export a function here, and we'll export it default, and we'll call this function configureStore because we will use this at the entry point of our application. The configureStore function should accept one parameter, which is the initiaState for your app. This is a good way to initialize your store with some state, especially when you're doing server-side rendering, which we're not going to cover in this course, but this parameter can be really useful if you're doing such a thing. Inside this function, we're going to return a call to createStore, which we imported up at the top. CreateStore will take two parameters, the first of which is our rootReducer, and the second is the initialState. So we'll close this with a semicolon. And that's all it takes to configure our store. But while we're in here, let's add an optional piece of middleware to enhance our store. Now to add middleware, we're going to need a function that comes with Redux called applyMiddleware. The third parameter for our store accepts the applyMiddleware function, so we'll just pass it right here. And inside these parentheses, we can specify all of the middleware that we'd like to utilize in our application. Now the middleware that we'd like to apply is reduxImmutableStateInvariant, so let's first add an import for it. And now that we have it available, we can just call it right down here in the applyMiddleware function. And make sure that we have parentheses here so that we actually invoke reduxImmutableStateInvariant within our applyMiddleware function. And, of course, if we had more middleware, then I could add other arguments right here to the applyMiddleware function. We'll see how to do that a little bit later. Now there's a variety of other interesting things that we could do to our store like add support for hot reloading or adding support for the Redux dev tools extension in chrome, but I'm going to leave those out here for simplicity. Check out React Slingshot on GitHub for an example of how to configure these other pieces of middleware. Great! So we've just added reduxImmutableStateInvariant as middleware for our store. Now that we've set up our configureStore function, we need to put it to use at our application's entry point, so let's take care of that in the next step.
Instantiate Store and Provider
Great! We have nearly all the major pieces of Redux wired up now. We've created our first action, we've created our first reducer, and we've set up our store configuration. The last detail is to update our app's entry point to work with Redux. So let's go over to the src directory and open index.js. The first thing we need to do is add an import for our configureStore function. So I will say import configureStore from './store/configureStore', and I can call configureStore right here. I will create a constant and call it store, and I'll just call configureStore. So now we've created an instance of our store. In this case, I'm not going to pass initial state to the store, but that is an optional parameter that I could pass right here. If you were creating a server-rendered app, you might choose to do so. Now let me explain. You might be confused about the difference between passing initial state here and setting initial state within our reducer. Currently, our reducer already sets its initial state using an ES6 default parameter, and we can go over to our courseReducer and see that occurring right here. We are setting the initial state for this reducer to an array. So you can imagine that each one of our reducers would handle their initial state right here in their method signature as a default parameter. So if I passed initial state here, what I'd be doing is overriding the default parameters that we specify in our reducers. The question is when would you pass initial state to this configureStore call? Well, if you're wanting to rehydrate your store using some separate state that's passed down from the server or stored in local storage, then this is a good place to do so. We're not going to do either of those things in this course, so I'm just not going to pass in a parameter here. Okay, with that clarified, let's move on. We now have a configured instance of our store that's set to a constant right here called store. But the question is what do we do with it? That's where a companion library comes into play. If you're using Redux with React, then you're going to want to use the react-redux library, so let me add an import for that as well. And I'm going to import a component called Provider from 'react-redux'. So react-redux provides this special component called Provider. What Provider is is a higher-order component that attaches our store to our React container components. So let's wrap our Router component with the Provider component. The way I can do that is come down here to our render function and reference Provider. Provider takes one prop, which is the store. And in this case, I will just pass the store in. Of course, we need to close our wrap right here, so I will close the Provider. And let me just fix my formatting a bit. There we go. As you can see, the Provider component accepts a store as a prop and just wraps our Router component. So, effectively the Provider component is wrapping our entire application so that it can be connected to our Redux store. So this is what our final application entry point looks like now that we've set up react-redux and our Redux store. And because our application is now wrapped in the Provider component, we'll be able to access our Redux store in our components. And the great news is this wraps up the boilerplate of our Redux configuration. Now it's time to put our store to use by connecting our first container component to work with Redux.
Okay, that was a lot of plumbing. Yes, I know it's a lot to take in. The good news is most of the work that we just did only has to happen once. And as you'll see, now that we have the infrastructure in place, adding additional behaviors won't take long. We now have all the Redux infrastructure set up except for this remaining piece, which is to update our CoursesPage component to work with Redux. To do that, we need to reference the connect function which comes with react-redux. So let's go over to our CoursesPage, and we'll go to the top, and I will import connect from 'react-redux'. And at the bottom of our component, instead of exporting a plain component, we're going to export a component that's decorated by the react-redux connect function. The connect function is what we use to create components that can interact with Redux. I'm going to refer to these components as container components. So let me update this call. Instead of exporting the CoursesPage, I'm going to export now the CoursesPage wrapped in a call to connect. Connect is a higher-order component that's going to wrap our CoursesPage. And connect takes two parameters, the first being mapStateToProps and the second being mapDispatchToProps. Each of these parameters is a function. And I know they're both a mouthful, so we'll discuss each in just a moment. First, you're likely squinting at the oddity of having two parentheses side by side right here. This is just two function calls. The connect function right here ends up returning a function, and that function immediately calls our container component right here with a result to the first function. If this style confuses you, let me just show you the alternative setup. See, I could create an intermediate variable. I could call connect and pass it the parameters, take the result of that first function call, which is a function, and then I could use that function to call CoursesPage. If you find that this reads a little more clearly, you could use this instead. But once you get used to reading this, it really gets pretty easy. What you find is this is a very common approach in functional programming, taking the results of one function and passing it on to the next function. So I will use this more terse style down here below. The first function that we are passing here is mapStateToProps, so let's define that function. MapStateToProps takes two parameters, the first being state and the second being ownProps. Inside this function, we're going to define an object that returns the properties that we'd like to see exposed on our component. So, for instance, if I say courses right here, then I'm saying I would like to be able to access my courses by saying this.props.courses up here on this component. Now, what I need to define though is how to get that course data. State, right here, represents the state that's within our Redux store. So to access this state, I can say state.courses, and now I am accessing the course data that's within our Redux store. Now to clarify, this property right here is determined by the choice that we made within our reducer. If I go over to our rootReducer, you can see that I called it courses right here. If I had instead called it courseReducer, then this would say courseReducer, and here I would have to say state.courseReducer. I don't like that. I like the clarity of being able to say just state.courses here. And that's exactly why over here in the rootReducer I aliased our reducer to call it courses instead. But I do like to put the word Reducer on the reducer, again, just so that the tab is named clearly and easy to pick out of a crowd. As you can see, mapStateToProps also takes a second parameter, which is ownProps. This parameter lets us access props that are being attached to this component. That's why it's called ownProps, because it's a reference to the component's own props. Now in this case, it'll be most useful for accessing routing-related props injected by React Router. We're not going to need that at this point, so we'll discuss ownProps more later. Of course, the second parameter to connect is mapDispatchToProps. That's a mouthful, but what it's really for is deciding what actions you want to expose on your component. Now this is an optional parameter, and for the moment, I'm going to go ahead and delete it. I'll just take it off. When we omit this parameter, something interesting happens. Instead, our component automatically gets a dispatch property attached to it, and that's injected by connect. So connect is saying oh, if you don't put this second parameter on here, then you'll be able to come up here and say this.props.dispatch. Now, what is dispatch? Dispatch is a function that allows you to fire off your actions., so I will be able to dispatch different actions that we've defined in our actions file over here in courseActions. To be able to dispatch an action, we need to go back to the top of the file because we need to create a reference to the createCourse action that we created earlier. So let's import that here at the top. And now that we have access to our createCourse action in our component, we can go ahead and update our onClickSave function. We can take out this alert, and instead, we can dispatch our first action. So I will say this.props.dispatch because remember, since we didn't define a mapDispatchToProps function down here, connect is going to inject a dispatch prop for us. This is the function that we need to call to be able to fire off an action that Redux will handle. Now we need to pass it an action. So I will reference courseActions.createCourse, and createCourse, of course, is going to need a reference to some data. We'll need to pass it some data, so we will pass it this.state.course. Now I will admit this is ugly looking right now, and I've very deliberately shown you the ugly way of getting this done. I'll show you a more elegant, more terse way of dispatching actions here in a moment. But I want to show you the different ways that you can use Redux because Redux lets you wire things up in a way that you prefer. This is the most verbose way to end up dispatching an action. So excellent! This wraps up our Redux flow. In the next clip, we'll update our CoursePage component's render function to display our data, and we'll be able to step through the whole Redux flow and see how this whole thing works.
Step Through Redux Flow
Our CoursesPage is now connected to the Redux store, and the list of courses is available on this.props.courses because we set up the mapStateToProps function down here. So there's one final detail before we jump over to the browser and try this out. Let's update the render function so that it displays the current list of courses. So right here under the Courses header, we can say this.props.courses.map, and we will map to this.courseRow. Now this is a function I haven't created yet, so let's go create that function right up here. And for that function, I'll call it courseRow, and it will take the course and the index as parameters. In here, we will return, let's just do something simple, we'll return a div, and we'll give that div a key of index. Of course, we need to have a key anytime we're iterating. And we will just display the course.title right here. And I'll close the div, put in my semicolon. So we can see we're just going to map over the list of courses and then call the courseRow function for each one of those courses. And if I hit Save, I should be able to open up the terminal here. And we'll say npm start and see how this goes. Okay, so what we can see is we're getting a few different linting errors, first in our courseActions file, which we updated in a previous clip. I do not have a semicolon on line 2, so I will save that. Now we can see our linting is passing. But this can be a bit deceptive because there were other issues on linting here that we need to address, which is that dispatch and courses are missing in props validation, so let's set up some validation right here. To clarify what's going on here is the output of eslint-watch is showing that this particular file is now clean, but that doesn't mean that these other issues up here are resolved yet. So we have to pay attention. Once we get linting errors, we need to go address all these linting errors. So I will just paste in some validation here. And now we can see that we are requiring dispatch and courses as prop types here on our courses page. When I hit Save, we can see that now CoursesPage is clean as well. So just keep that in mind with eslint-watch. This can be confusing, but when you first have a linting error, it's going to list all those errors. And then as you fix an error for a given file, it will report that file as clean. But that doesn't mean that everything is clean. It means the file that is recording here is now clean. Another way that you could, of course, do that would be to kill your process and bring it back up. So I could close my terminal, for instance, or hit Ctrl+C to kill it, and then I could bring it back up again, and I should find that when I say npm start again that we still get no linting errors. Then I know for sure that I've gotten all of my linting errors taken care of. And we can see that they are all still clean, and all our tests are passing. Although we shouldn't celebrate the test passing too much. Remember, that was just a cheesy return true=true. Okay, now we should be all set to jump into the browser and see if this works. So we'll go over to the Courses tab here, and I should be able to enter a course and hit Save. And there we go. Now we can see our courses are adding successfully. Of course, it would be nice if we cleared out this form, but we were really just doing this just to test the flow of Redux. And I should be able to open this up. It looks like I've got a failed load. I think we can ignore that error. Let me just reload and make sure that's the case. Now we have no errors showing in the console. Good! Looks like we're in good shape, courses adding as expected. So we're running through the full Redux flow. Now this is a good time to set some breakpoints just so that we can see how we walk through the Redux flow. So let's jump over to the code now and take care of that. I'm going to come back into here and set breakpoints at each step in the flow. Let's set a breakpoint here in our createCourse action creator. And then, we'd also like to see this handled over here in our courseReducer, so we can set a debugger right here in this case because we're expecting the createCourse action to be called. And then, of course, over here in our CoursesPage component, we would expect that when the course data changes that this mapStateToProps function would receive that new state and end up passing that state as this.props.courses to our component. And then, of course, finally, we would expect the render function right here to be called after that occurs. So now that I've added all these debug statements, we can see linting is pretty cranky at this point because we are checking for that. But, of course, we'll take those out in a moment. I'm going to refresh just so that we have the debugger reflected here within our code. Alright, so let me just step past all of these initially. And now what I want to do, of course, our render function's going to get called every time I hit a key. But now I'm going to hit Save, and we'll be walking through the flow of Redux. The first thing that we see here is we're landing in the action creator. So there's our action creator getting called and getting passed this course with a title of t. And I'll hit F8 to continue. And now we're landing over here in the courseReducer. We can see that state right now is empty. There're no courses in state because this was initialized to an empty array, and the action in this case is CREATE_COURSE. So we're going to fall into this part of the switch statement, and we will use Object.assign to take this course and then add it to the array, which is currently empty. I'll hit F8, and now we land over in mapStateToProps. So now we're back at the bottom of our CoursesPage function. We can see that state right now has one course, the course that we just added with the title of t. OwnProps, in this case, has all the data about the props that belong to our container component. But you can see that these let us get different information that's related to the URL and our routing data. Then finally, when I hit F8, we land up here in the render function, and now we should find that courses has one element in it. So now our array is populated with that data. And that completes our Redux flow. React rerenders and shows this course right here. Great! So we just saw the entire Redux unidirectional flow. We're dispatching an action right here within our CoursesPage. That's landing over here in our createCourse action creator. That's handled in our courseReducer right here looking for that particular action type. And then we land back over here within our mapStateToProps function where we pull the state and map it to our courses property. Then finally, the new render function is called right here. After, our mapStateToProps ends up injecting new data for our component. So now we have a nearly complete view of Redux. Not bad for so early in the course. But I mentioned that there's a cleaner way to handle mapDispatchToProps, so in the next clip, we'll check that out.
mapDispatchToProps Manual Mapping
When we wired up the call to dispatch the createCourse action earlier, I mentioned that there was a cleaner way to get it done. How? Well that's accomplished with the second function that we pass to connect, which is mapDispatchToProps. Remember, we left it out down here initially. The mapDispatchToProps function determines what actions are available in the component. I'm going to add a reference right here and then define it right here. Say mapDispatchToProps. And mapDispatchToProps takes one parameter, which is dispatch. This will get injected in by the connect function. Now as I said, this function determines what actions are available in our component. So in this function, we will also wrap our action creators in a call to dispatch. And let me show you how we'll get that done. There's actually a couple of ways to do it, but for now I'm going to do the mapping manually just so that you can see what I'm doing. Of course, the action that we want here is going to be createCourse, so we will call it createCourse, and we will define an arrow function. That arrow function will take a course as its sole parameter. Of course, with arrow functions, you can omit the parentheses for the arguments when there is a single parameter, so I'm just going to omit this here. What we're declaring here is an anonymous function and using the arrow function syntax, and I'm going to call dispatch and then call courseActions.createCourse, and I will pass it the course. So this call ends up replacing what we had up above. It moves the noise that we created here down to here and replaces it with something a little bit different. I prefer this approach because now it means that we can come up to here and say this.props.createCourse and just pass it the course. So now our call here is much cleaner. It's already wrapped in dispatch for us down here in the mapDispatchToProps function. So all I'm really doing here is wrapping our action in a call to dispatch so that it's easy to use up above in our component. I'll show you an even simpler way of handling this is in a moment. Now note if I didn't wrap this in a call to dispatch, then nothing would happen up above. Right here, if I called this.props.createCourse, what I would end up calling would be this function, which returns an object. This object by itself would do nothing. I would just end up holding onto a reference to an object. What we need to do is call dispatch, and that's exactly why we have this call to dispatch here. So we wrap our actions in a call to dispatch, and that triggers our flow through Redux. And now that we have this mapping set up, we've declared that our component above will receive createCourse as a prop, and it will be wrapped in a call to dispatch for us. So let's open the terminal back up and just make sure everything is still working. Now you will note though that we have a couple of linting issues. CreateCourse is missing in props validation, so we can add that in. And we also need to fix our missing semicolon down here on 66. Now we should be able to come over here to our browser and just make sure that we can still add course. Now that is still working, but we should note here that the required prop dispatch was not specified in the CoursesPage. This is important to note because dispatch is no longer injected as a property now that we've defined the mapDispatchToProps function. And I'm going to say that again because this is an important quirk that may confuse you at first. Once we started defining the mapDispatchToProps function, connect will no longer add a dispatch property on our component. So this is no longer getting injected. Of course, that's okay because now that we've defined mapDispatchToProps, we no longer need to use dispatch in our component. We're using it right here in mapDispatchToProps, so this is a completely logical thing, but it is something that can confuse you at first. So now I can take out the props validation on dispatch, and I should be able to come over here and say yes, the app's up to date, and we no longer have any issues or warnings in our browser. So I just showed you the second way to handle mapDispatchToProps by defining createCourse. The first way that we looked at, to clarify, was just omitting mapDispatchToProps altogether since it's an optional component. And in that case, we used dispatch directly. Here I showed you manually using dispatch and doing your map here. In the next clip, I'll show you a third way that's a little more terse.
We just saw how using mapDispatchToProps helped simplify dispatching our action within our component, but our call here in mapDispatchToProps is still quite verbose. Redux comes with a helper function to help save us from having to manually wrap our action creators in a dispatch call. This function is called bindActionCreators. So let's import it up here at the top. I'll say import bindActionCreators from 'redux'. Now we have it available to use down here below. And the way this works is instead of doing this, I will call bindActionCreators, and it will basically do that work for me. I'll pass it the courseActions and my dispatch parameter. And what bindActionCreators will do for me is it will go through my courseActions and find all the actions in that file and then wrap them in a call to dispatch. Now, since it's going to be all those actions, I should change this parameter name to actions because now we'll be mapping to all of the actions that sit in the courseActions file. Given right now there's only one, but this is a handy way to go. I often use this pattern because I like expecting my actions to sit on this.props.actions. It helps separate my actions from my courses. Now, of course, that means we also need to update our PropTypes validation on our component, and it means that up here no longer will I call this.props.createCourse because it will sit under actions. I need to say this.props.actions.createCourse. To clarify, I didn't have to add actions as this extra object in here. I could have mapped a specific action out of here. I could say courseActions.createCourse, and then I could've left this as createCourse. But I'm going to go the route of keeping our actions under actions. I just like this pattern. So I'll pull this back out, and we will just map all of the course actions and wrap them in a dispatch. So I find the bindActionCreators function is a handy way to reduce the amount of work that's involved in mapping my dispatch to props. Now let's make sure it worked. Come back over here, and I should still be able to add new items. But it looks like Cannot read property 'createCourse' of undefined. Let's see. I'm going to try refreshing. Ah, Invalid prop 'actions' of type 'object'. Ah, there we go. So this should be an object now because actions is an object. And I see the mistake that I made now. When I updated my PropTypes validation, I actually switched things here because I should've called this one actions, and I accidentally changed courses here. We need courses as our validation, and courses continues to be an array, and then actions is an object that is required. These are the two properties that we're expecting now is courses and actions. Now we should find that our linting is happier. And we can come back over, test, and we can see that things are adding just fine. Now before we move on, just one final note. I want to clarify that there is nothing magical about the names of these functions. You can call them whatever you like. And in fact, I've seen other people use other function names that are more terse that they find more clear. I don't mind these names, and you'll find these in the Redux docs. So I tend to use mapStateToProps and mapDispatchToProps as my names for these functions. And, of course, if you prefer, you could even define these inline. Since all these functions do is return an object in each case, you could define these objects right here inline, although I personally prefer to have these separate named functions. I find it easier to read. I like this pattern. So, throughout the course, you're going to find this structure in the container components that we create. Alright, so we've now seen three different ways that you can handle dispatching actions in your container components. In the next clip, let's step back and review the structure of a container component.
Container Structure Review
So now that we've created a container component that utilizes all the core pieces of Redux and react-redux, let's review the five major pieces of a container component. First, of course, up here at the top, we have the constructor. In the constructor, we're going to initialize state and also call our bind functions. Any functions that need to be bound to the this context, this is the best place to do so. Then we have our child functions, which are called by render. Then we have our render function where we would typically just be calling a child component. But here for simplicity, I've just put the markup inline. I recommend keeping the markup separate. Container components ideally just call a child component that contains that markup. And we'll move to that pattern throughout the rest of the course. And then we have our propTypes that provide our prop type validation. Then finally, we have our Redux connect and related functions. So we have our call to connect. We have our mapStateToProps function and our mapDispatchToProps function. Alright, to wrap up this module, we still have one tweak to make. Instead of hardcoding strings for action types, we should use constants, so in the next clip, let's make that tweak.
Action Type Constants
Redux errors on the side of being unopinionated and explicit. There are some options for reducing the boilerplate if you're interested. Check out the Redux docs after you get more comfortable. There's a section on reducing boilerplate that discusses alternative approaches. Now you likely cringed earlier when I used hard-coded strings for action types. And hey, if you didn't, you should have. Magic strings are just typos waiting to happen. Just like in Flux, we should avoid typos. We really should use constants instead. And there're a couple of different ways of handling this. We could create a constants folder over here with a dedicated constants file inside where all your action type constants are stored in a single spot, and that way you don't clutter up your action types file. But the downside is it's yet another file that you have to open and edit every time you create a new action. Placing your constants within your actions file, for instance, placing my declaration to a constant right here above my createCourse action creator is more convenient, but there are a couple of downsides. First, it would add noise to my courseActions file, and second, when I want to use the constant, if right here I said const CREATE_COURSE = 'CREATE_COURSE', now when I want to use this constant, I would have to reference courseActions, which means I would have a reference to my courseActions from my reducer over here. You will see various people use this approach, but I personally prefer to keep my actions in a separate file, so that's what I'm going to do here. But I'm going to compromise, and rather than putting them in a folder called constants, I'm going to define my action type constants right here in actions in a file called actionTypes. As I said, there's no right answer here, just two options with some tradeoffs to consider. But I find it more logical to place action type constants in the actions folder rather than out here in a separate constants folder. And now that we have a constant, let's update our courseActions to use the constant. So we'll need an import here. We'll import * as types from './actionTypes'. And then right here, instead of having a hard-coded string, I can now say types.CREATE_COURSE, although that's not working. Ah-ha! That's why. It's very important when you define your action type here that you export it. We'll add other action types over time, but we need the export keyword so that it is available over here. I was wondering why I wasn't getting IntelliSense support there. There we go, types.CREATE_COURSE. And we can also use this on the other side in our reducer. So we'll come over here, and, in fact, I'm going to be lazy. I will copy this import statement and will paste it into our reducer as well, although, of course, the path is now different. We need to go over to actions to actionTypes. Is this now right? That should work. Now we need to say types.CREATE_COURSE. So now on both sides, instead of using a string, we're using our constants instead. This helps us avoid typos along the way.
Alright, if we were pair programming in person, I'd give you a high five right now because you are over the biggest hurdle. We just created a complete Redux flow using actions with action type constants, a Redux store, our first reducer, and a container component that's connected to our Redux store. Now there're a lot more details to explore in the next modules, but you've now seen the fundamentals and implemented it yourself. Bravo! However, there's a significant common use case that we've ignored so far. How do we handle making asynchronous requests like AJAX calls to the server? In the next module, we'll explore that by loading up existing course data via an API on page load.
Async in Redux
So far, we've only created asynchronous actions, so what happens when we want to do asynchronous activities like make AJAX calls to the server? Here's the plan for this module. We'll begin by discussing the merits of creating and using a mock API throughout development. Then we'll discuss the various libraries available for enabling asynchronous flows in Redux. Once this foundation is set, we'll be back in code for nearly all the rest of the course as we implement asynchronous flows using redux-thunk. Let's get started by considering the merits of mock APIs.
Why a Mock API?
Now instead of hitting a real API, I've created a mock API that simulates making async calls to the server. I did this for convenience here so that we don't all end up hitting the same API and wiping out each other's data. That's no fun. But I actually utilize and recommend the mock API pattern any time I'm building a client-side app. Here's why. First, this pattern allows you to start development immediately, even if the APIs that you need to consume haven't been created yet. As long as you can agree with the API team on the shape of the data that the final APIs will return, then you can create a mock API and begin development. A mock API helps me move independently when a separate team is handling the web APIs. We don't have to move at the same pace. This means I'm not directly reliant on other developers delivering code in order to build the UI. Now if I'm also building the APIs, then I get to decide when to do so. It's no longer a blocking issue for building the UI. That makes life much easier for both teams. It's effectively the rule of coding to an interface rather than an implementation. A mock API gives me an easy backup plan if the API is down or broken at any given time. I don't have to stop development. I can just point to the mock API and keep working. Hitting mock data is also the fastest way to handle rapid development because you can count on all responses being instantaneous if you like. This means that you're not hampered by slow or unreliable API calls in the early stages of development. Now you might be thinking yeah, that could also mask performance concerns. That's certainly true, but of course, you'll test against the actual APIs before deploying. And the good news is you don't have to wait until the real APIs are complete before testing how the app feels with slow APIs because unlike a real API call, a mock API allows you to control the speed of responses. You can get a feel for how the app performs when the API calls are really slow or really quick just by using setTimeout within your mock API to delay your responses. A mock API also gives me a handy tool for automated testing. Since the data is local, it's both fast and reliable. You don't have to mock calls since your mock API is already a mock. And since the data is deterministic, you can even write tests that utilize the data, and they won't be slow since the tests are local. All the data is just sitting in memory. Finally, you can easily point to the real API later by simply changing the import at the top of your file, or you could even check a centralized config that allows you to toggle between the mock and real APIs via a single setting. For all these reasons, I prefer to always create a mock API for my projects.
Async Library Options
Normally, we can only return objects from our action creators, but with redux-thunk, we can return a function instead. Here's an example of a thunk for deleting an author. As you can see, a thunk is a function that returns a function. Thunk is actually a computer science term. A thunk is a function that wraps an expression in order to delay its evaluation. So in this case, the deleteAuthor function is wrapping our dispatch function so that dispatch can run later. Depending on your programming background, returning functions from functions may feel strange, but it's a common and powerful technique in functional programming. On this third line, I'm calling a regular action creator called deletedAuthor. But note that you don't have to call a separate action creator function. You can simply inline the action within the thunk if you prefer. This action creator's only going to be used in this one spot, and that is often the case when you're working with thunks. Alright, enough talk. Let's get thunky. Sorry, I couldn't resist. Anyway, it's time to jump back into the editor and create our first thunk.
Mock API Setup
Before we start handling asynchronous calls with Redux, I need to assure that you have the mock API that we're going to use. Now if you downloaded the starter kit, you'll see that there are three files in the API folder, the mockAuthorApi, the mockCourseApi, and a file called delay. I don't have these files in my solution yet as you can see here. Now you probably do because I'm guessing that you used the starter kit at the beginning of this course. Since I didn't use the starter kit to start my project though, I need to create these files now. And if you didn't use the starter kit, you'll need to create these files now as well. So I'll go ahead and do so. I'm going to create a new folder underneath my src directory and call it api. And now I'm going to create three files within here. We'll call the first one mockAuthorApi.js, call the second one mockCourseApi.js, and we'll call the third one delay. This third file is called delay because it manages the simulated delay of each mock API call. Let's start with the mockAuthorApi. I'm just going to copy it and paste it over into the mockAuthorApi file. And I'll do the same thing with the mockCourseApi. Again, click on Raw, copy this data over, and go to the mockCourseApi. And then finally, I need to get the delay code, which is really simple, just a one liner here where I'm exporting default 1000. We'll put this in delay. Alright, so I've now configured my mock API. You don't actually need to understand what's in these files, but as you can see, at the top of both the mockAuthorApi and the mockCourseApi is some hard-coded data that's really just simulating a database that sits on the server. And as these names infer, these are mocking an API. So we're going to pretend that we're making AJAX calls to a server and that it's sending back data that it's retrieving from the database. But what these files actually do is use setTimeout to simulate the delay of making a call to the server, and then they return the data for me. You can see I'm using delay here, so each of these calls uses setTimeout. You can see that our API is pretty simple. It allows me to delete an author, save an author, get all authors. We're not going to use all of these right now, although those will be part of the challenge at the end of the course. We'll focus more on the mockCourseApi, which will let us do things like get all courses, save a course, and delete a course. Again, you don't have to look through this code and try to understand it. This is really just simulating an API call. And our delay right here is set to 1000, which means any call that we make to these mock APIs is going to take 1 second to respond. I find this is a nice compromise. It's slow enough that it lets me see the simulated delay that someone might have on a slower connection or if our API calls are a little bit slow, but it's not so slow as to annoy us. And, of course, I could set this to 0, and then all my API calls would seem absolutely instantaneous. You can certainly do that if you prefer. I like coding with a little bit of a delay here because it reminds me of usability issues that occur when my API is a little bit slow. I find that this helps avoid ugly surprises later when I realize that the user experience really goes downhill when my API gets slower. Okay, so with this out of the way, now we can get started by looking into how to manage courses from a separate ManageCoursePage.
Remove Inline Form
In the previous module, we set up our first data flow through Redux. We created a very simple course that consisted of nothing but a title. In this module, we'll begin by restructuring our work to better separate concerns. There are some fundamental problems with our current design. First, CoursesPage should be a container component. I'm going to open it up so we can look at it. It's connected to Redux, and thus, it should ideally not have much JSX inside. But you can see that we've put all the JSX right here in the render function. Second, it's also currently both displaying courses and supporting adding courses. For clarity, let's handle the adding and editing of courses on separate pages. So we can create a separate component called manageCourses. Let's begin by removing the Add Course form that we created in the previous module. It was useful to show the Redux flow, but it really doesn't belong here long term. To do so, let's remove the Add Course header, which I just did, and we'll remove the onClickSave function. We'll remove the onTitleChange function, and we'll remove the corresponding binds that are in the constructor. Finally, we can remove the state initialization that's happening here as well because this page is no longer going to be concerned with managing courses. It's just going to show the list of courses. So we'll have a separate page where we manage course data. And by the end of this module, we'll have an application that allows us to view, create, and update courses all using Redux. And for now, we should still be able to run the app just fine. It's just that when we load the CoursesPage it's going to be empty instead. So instead, let's turn our attention to displaying a list of existing courses on initial load. To do this, we use the mock API that we just set up to fetch a list of existing courses. We'll load the courses by displaying an action with Redux. And to make this happen, we're going to use Redux thunks.
Add Thunk to Store
One of the first questions that people have when working in Redux is how do we handle asynchronous calls like AJAX calls to an API? As we discussed in the slides, the two most popular approaches at the moment are thunks and sagas. In this course, I'll use thunks because they're simple and effective. The first step to using thunks is to enhance our store configuration. As you can see, I'm here in the configureStore file. And to add thunk to our middleware we, of course, need to first import it. And once we've done so, we can just add it here to the list of arguments that we're passing to applyMiddleware. It's really as simple as that. So we could pass as many pieces of middleware as we want to the applyMiddleware function. We could, of course, add other middleware at this point, and the Redux docs list a number of interesting pieces of middleware that you can consider for things like logging, scheduling actions, and sending crash reports when issues occur. But now that we've updated our store to utilize thunk middleware, we're all set to create our first thunk.
Create Load Courses Thunk
We just added Redux thunk to our store configuration, so it's now part of our Redux middleware. So we're all set now to create our first thunk to handle making an asynchronous call. For our first thunk, let's create a way to load courses when the app initially loads. We can begin by opening the courseActions file, and I like to put my thunks at the bottom of the actions file, but you can always place them in a separate file if you prefer. We'll create a new function called loadCourses. Now let's think about what needs to happen here. We're making an asynchronous call to our API, so we'll want to handle the promise and then dispatch an action when the promise is resolved. To get started, we obviously need to add a reference to our course API since we're going to call it in our thunk. So I'll come up here, say import courseApi from /api/mockCourseApi. And next, we need to add some boilerplate that's necessary for each thunk. Remember, a thunk always returns a function that accepts a dispatch. So let me add that in now, return function(dispatch). So this wrapper function will exist in every one of our thunks. Now we're inside the body of our thunk. At this point, it's a pretty logical spot to go ahead and make our API call, so I will say return courseApi.getAll. Oh, I pulled in the wrong API. I referenced the author API. Let's change it up here to CourseAPI because we want to get course data right now, and I will say getAllCourses. GetAllCourses returns a promise. And if you go into the mock API, you can see that. I'm doing that because often that is how we want to wire up any kind of a proxy with our API is to return a promise. So I can choose to handle this promise right here, and I can say then I'm going to expect getAllCourses to return a list of courses, and then I will handle it here within an arrow function. Again, since there's only one parameter for this arrow function, I can omit the parentheses around this parameter. So really, we just have an anonymous function here. And after this returns, we're ready to dispatch an action creator. So let's dispatch something called loadCoursesSuccess, and we will pass it a list of courses. Now that function I just referenced doesn't exist. We don't have an action creator called loadCoursesSuccess yet, so we need to go ahead and create it next. Before we do, though, I am going to add in some error handling here. There's a few different ways you could choose to handle this. You could have a dedicated action creator that handles any errors. But for now, I'm just going to use a .catch on my promise. And in here, I'll just go ahead and throw the error so that it gets just thrown up the stack. Now again, as you can see, I'm calling our mock API, but in a lot of examples, you'll see people making a fetch call or an AJAX call right here within the function rather than calling some separate proxy. Either approach works fine. Of course, for the reasons I mentioned earlier, I really prefer working against a mock API. Having that extra layer of abstraction is really helpful from my experience. And the nice thing is with this setup, if we want to hit a real API, we only have to change the import at the top of this file to point to a real API instead. Okay, with that caveat out of the way, let's get back to finishing our work on this thunk. I haven't created the loadCoursesSuccess action yet, so let's do that. First, let's add a new constant in here. And in fact, I'm just going to change this one because we're not using it anymore, and I will say LOAD_COURSES_SUCCESS, and I'll copy this over here. So now we have our new constant. I'm just going to use this action creator since we're not using it now and just rename it. I'll call it loadCoursesSuccess, and it's going to take a list of courses, and the type it's going to use is LOAD_COURSES_SUCCESS. Of course, instead of it being an individual course, it will be courses. So now we have our loadCoursesSuccess action creator. Now you've noticed I'm putting SUCCESS here on the end of my action creator name. This seems like a good place to pause so that we can discuss action naming conventions, so let's do that next.
Action Naming Conventions
Alright, let's talk for a moment about action naming conventions. Here I'm using the Success suffix for two reasons. First, we already have a function called loadCourses right here, our thunk. Second, this action doesn't fire until all authors have been successfully returned by our API call, so the suffix helps clarify that our async request was successful. Third, people often create a corresponding failure action type called loadCoursesFailure or loadCoursesError. To help save us time and typing, I'm not going to create a corresponding error action for each thunk, but you might want to do so in a real app when you need to treat the failures of different async calls uniquely. I'll show you my approach to handling async errors in a little bit. But for now, I'm just going to use catch on my promise and then throw that error. Alright, with that out of the way, now we're set to discuss how we're going to handle loading courses in our reducer.
Load Courses in Reducer
Now that we have a loadCoursesSuccess action, we need to create a corresponding handler over in our reducers file., so let's open up our courseReducer file. And instead of calling CREATE_COURSE here, I'm just going to use this case statement now to handle our LOAD_COURSES_SUCCESS action since that's all we're going to do for now. And it's going to be quite simple here. The only thing I need to do is return the courses that are passed in on the action. Since whatever's returned from our API will simply replace what was in our state, this is all we have to do. Simple as it gets. So now we have our new action set up and a corresponding reducer that handles the LOAD_COURSES_SUCCESS action. But the question is where and how do we fire this off on load? So let's set that up next.
Dispatch Action on Page Load
To fetch the course data when our app loads, we can open our application's entry point, which is index.js right here. In here, you can see the call to configureStore on line 11. Now once the store is configured, we can go ahead and dispatch actions against the store. So let's add a reference up here to our action. We'll say import loadCourses from './actions/courseActions'. And here I'm just choosing to use destructuring just to keep this call a little bit shorter. Totally optional, just if you want to. Now that I have a reference to my store on line 12, I can go ahead and use the dispatch function, which is part of my store, and I can pass it the action that I'd like to dispatch, which in this case is loadCourses. Make sure you have these parens in here so that your loadCourses call will actually be called when you call store.dispatch. Of course, there are many alternative ways to handle this situation such as injecting JSON into the head of index.html via a server render. Then you would know that that data is there on your page. But I'm just showing an approach here that requires no server rendering. Great! So with this final configuration, we're now all set to try loading our app to see if the initial list of courses is displayed. Let's fire our app back up and see what happens. And before we do, it looks like I do have one typo in my courseActions. I am missing a semicolon on 15. Okay, now our linting is reporting back clean. So let's come over here and click on Courses. And excellent! Looks like we're in business. We've got the list of courses displaying right here. If I hit Inspect, looks like we're in good shape. Hot module reloading enabled here. And all we're doing is just displaying a simple list of courses, but the good news is that it's loading on page load. So I can hit Refresh, and we will see a wait of about a second before it pops in because we're waiting for that mock API call to come in. And we wait, and then it happens here. So excellent! We're in pretty good shape. Of course, right now we're only showing a small portion of the course data that we have coming back from our API, so in the next clip, let's create a dedicated component that shows more of this data in a more pleasant way.
Create Course List Component
Our CoursesPage component is a container component, so ideally this markup should sit in a separate component, a presentation style component rather than a container component. So let's create a new component called CourseList. I'll create it here within the course folder, and I'll call it CourseList.js. I'm just going to paste this in. There's really nothing particularly new or interesting in here. You can see that I'm also pulling in a CourseListRow, which we'll create in a moment. But I have a table with a header, we have our different headers for the data, and then I'm napping over the course data that's being sent in. You can see we have courses right here. And for now, we're not going to have a function for deleteCourse, so I'm just going to take that out. And then I have my PropTypes down here. But you can see that we're mapping over the list of courses and then using a CourseListRow component to display the individual rows, so we need to create that as well. Let's do that next. So I'll come back over here and right-click, and we'll call this CourseListRow.js, and I'll paste this one in as well. Again, you can see I'm using destructuring to keep my prop calls nice and short down here so I can just say course.watchHref and course.authorId, course.category. So now we're really just displaying a single row of data. Now sometimes you'll see someone right here go ahead and map and put this markup inline. That, of course, works too. I just prefer to have a separate component because I find that this reads really clean, and I like being this explicit. It is a little extra work to pass down this data on props, but I think it's worth it. And now that we have these new components that will display our course data, let's put them to use by jumping back over to the CoursesPage. Of course, the first thing we need to do is import our new CourseList component, so I will import CourseList from './CourseList'. And then here in our render, one thing I enjoy doing is using destructuring just to keep my calls short within my render, so I'll say const courses = this.props. Of course, this is totally optional again. But now that I've done that, I can just call my courses component or my CourseList component right here and pass it my list of courses. So it keeps this call nice and short. Otherwise, I would say this.props.courses. Now with only one call here, this technique seems a little silly. But as we add more down here, it is useful to go ahead and destructure anything that you want to use so that you don't have to repeat yourself saying this.props multiple times down here in your render. Great! Now let's load up the browser and check out the results. Ah-ha, now that looks much better. You can see that it takes a moment to pop in when I hit Refresh just as before. But once it does, we have all our data. This is why I enjoy working against a mock API. I can ensure that there's enough delay that these sorts of things are obvious. This isn't the ideal interaction because I can see the header, and I can see part of my table. But having it pop in this way isn't ideal. This is something that we can deal with at a later time though. Adding a preloader would certainly be a nice thing to do. Now, if we click on any of these titles, the anchor takes us nowhere. We can see the URL is updated, but the anchor doesn't actually work, so we need to wire up the ManageCoursePage so that we can edit and create new courses. And that's exactly what we'll do in the next module.
In this module, we discussed approaches for handling asynchrony in Redux. We saw that there are multiple ways to get it done including thunks, redux-promise, and sagas, but we're using thunks for this course because they're easy to learn and use. Once you get comfortable with Redux and thunks, I highly recommend looking into sagas as a powerful alternative strategy that relies on ES6 generators. We discussed approaches for naming async calls such as having a suffix of SUCCESS and ERROR so that you can handle success and failure explicitly from your API calls. And we created our first thunks, which we used to load course data. Now that we're comfortable with handling async calls for loading, it's time to add support for creating and updating courses to our app, so that's coming in the next module.
Async Writes in Redux
Now that we're comfortable using thunks to load course data asynchronously, let's put these new skills to use for creating and updating courses. In this module, we'll create a full course management page that allows us to asynchronously create and update course data. We'll compose the form by creating a few reusable form inputs, and you'll see how to populate the form using our Redux stores data via a combination of mapStateToProps and componentWillReceiveProps. Alright, time to get back to the editor. Let's keep coding.
Create Manage Course Page
We now have courses displaying nicely, but we want to be able to add and edit courses as well. We're going to handle adding and editing courses on a dedicated page, so we need to create a new component. This is going to become a pretty complex component, so let's begin with a simple shell. I find it helpful to set up my editor to create the basic structure of a container component for me, so here I'm going to use WebStorm's file template feature to create a new Redux container component. I'll just right-click on my course folder, go to New, and you can see down here that I've created some different file templates. I'll select Redux Container Component. It's going to prompt me for the name of my component, and I'm going to call it ManageCoursePage. I'll hit Enter, and when I do, now we can see that it created the component for me with all this boilerplate already set up. I knew that I needed React and react-redux and Redux itself and a reference to bindActionCreators. I knew that I wanted a class. You can see that it also even honors placeholders that I've set up, so the ManageCoursePage name that I selected for the file is also used for the class, used down here in my PropTypes, and used down here in my export within connect. So this helps save me a lot of typing on creating a new component. I really like features like this just to get things going quickly and avoid silly mistakes. So let me show you how I did this. If I go into Preferences, which on Windows I believe is called Settings, I can go down here to File and Code Templates and click on any of these file and code templates. I created this myself and gave it this name, and then I used this code inside. You can see that I used placeholders for $NAME, which gets injected. $NAME is the file name that I selected, so that's why you see $NAME in some of these places. But this is my simple file template that I like to use for creating Redux container components. I also have a simple one here for just a regular Redux ES6 class and another one as well for React stateless components. So these are just nice little shortcuts that I like to set up. As you can see, my component also contains some PropTypes setup, although they're commented out because we really don't know what PropTypes we'll need, but this works as a nice little reminder template. And we do have a compile error just at the start here. The assumption is we're going to return something in our render. It's just not there yet. Now the one thing that it, of course, doesn't include is the actions that we are going to need. In this case, we're going to need to import * as courseActions from, I'll have to go up a couple of levels, actions/courseActions. And then I can reference courseActions down here in my mapDispatchToProps call. This will make all the actions defined in courseActions available to this container component. Of course, I need to change this to say courseActions instead. Now all the courseActions will be available under this.props.actions. Also remember we discussed alternative ways to handle mapping DispatchToProps earlier, so you don't have to use the bindActionCreators method here. So feel free to use one of the alternative approaches we discussed in a previous module like binding to dispatch manually. Now since we just created a new page, we need to update routing to support it, so let's open routes.js and add the route. We'll obviously need another line here, so I'll copy this over. And the page is going to be just course instead of courses, and we're going to reference the ManageCoursePage. Now of course, that means I need to import the ManageCoursePage that we just created. Oh, it is singular, not plural, so I should say ManageCoursePage from './components/course/ManageCoursePage'. And we also need a path, or I shouldn't say path. We also need a route to handle a placeholder when we are trying to edit an existing course, so I'll put an id right here. We'll assume that second segment of the URL contains the ID. But in each case, we will map to the ManageCoursePage component. And finally, let's go back here to our ManageCoursePage and just put something inside. I'm just going to put an h1 that says Manage Course. So we at least can hit Save, and it looks like this compiles. And now that we've created our initial ManageCoursePage, let's see if we can jump over to the browser and load the page. We go back to Courses. When I click on a course name, there we go. We are loading the new page. Now given, we're not seeing the course that I've requested because we haven't written that code yet, but the ManageCoursePage that we've created is loading successfully. So in the next clip, let's create the form for managing courses.
Create Manage Course Form
Now we deleted the simple inline CreateCourse form that we created earlier in the course because it was very simple and not scalable. Now it's time to create a more real-world form that utilizes child components to help enforce enhanced styling and functionality. So let's go over here to the course folder, create another new file, and we'll call this CourseForm.js. And inside, I'm just going to paste this in. Now as you can see, this is a stateless functional component, and it calls some child components as well. We have a TextInput, a SelectInput, and then some other TextInputs for things like categories, length, and submit. One thing to note here at the top is the pattern that I'm following here. When I create stateless functional components, I prefer to destructure all the props here in the function's argument list. Again, this keeps the calls nice and short, and it also has the added benefit of making the component's dependencies clear. You can glance at the function signature and clearly see what's required, even if PropTypes aren't defined. As you can see, it uses some additional components like SelectInput and TextInput, which we need to go create. The other pattern that I often follow here is I should see that the PropTypes down here, we have 1, 2, 3, 4, 5, 6, should mirror exactly what we have up here. I should have the same number of destructured props that I have down here under my PropTypes. So now that we've created our CourseForm, let's create the TextInput and SelectInput components in the next clip.
Create Form Input Components
Now we need to create some reusable components for handling TextInputs and SelectInputs on forms. Since they're common components, I'd like to place them here in the common folder. Let's handle the TextInput first, and we'll call it TextInput.js. I'll just paste this in. Nothing too special going on here. The most important thing to notice is the nice thing about us creating this. The big benefit is there're a lot of Bootstrap-specific classes that are now being handled automatically anytime that I use a TextInpu, things like form-group, form-control, this wrapper div. These are all useful conventions that I get right out of the box by using this TextInput control that I've created. I'm also consistently showing errors right here, if any exist, and using some Bootstrap classes to style those errors. So now that we have our TextInput, let's go ahead and create a SelectInput as well. So again, I'll come over to common, say New, File, and call it SelectInput.js, and I'll paste this in. Here's our SelectInput. Again, these are basically the same components that we created in my React and Flux Pluralsight course, so I'd suggest checking out that course if you want to see closely how these were created slowly by hand. Now what's handy about these components again is that they contain all this Bootstrap-related markup for me. So I have my conventions built in. So this looks very similar to our TextInput. It again is going to display the error right below, so it is enforcing those UI conventions for me. And now that we have our ManageCourseForm set up, we can shift back to the ManageCoursePage and put it to use. So we'll do that in the next clip.
Use Manage Course Form
Now that we've created the necessary components, let's reference ManageCourseForm over here in ManageCoursePage. To do so, of course I need to add an import to the top of the page so that we now have a reference to the CourseForm. And now right here under Manage Course, I can, of course, say CourseForm, and I will want to pass it this.state.course. Now we do see some squigglies here, and that's because I, of course, can only have one top-level element in my JSX, so I need to define a wrapper div right here. Let me just fix my indentation a bit. There we go. Now it's happy. Now we know we need course data, so let's update mapStateToProps to pass an empty course just to get us started. And later, we'll filter existing courses from state as well. So down here in mapStateToProps, I'm going to just go ahead and paste in an empty course. And here we go. As you can see, just a lot of empty fields here, but this is our core course structure. And then, of course, instead of just returning all of state, we're now going to return a course. We'll return the very course that I just defined there. And you notice my indentation is a little bit off. That's just because when I created this file with a template it didn't honor my .editorconfig setting, so I'm going to just hit Windows Alt+L, and that will fix my indentation. Now everything is indented using two spaces, just like my .editorconfig specifies. Now we need to pass down mutable state to our form, so let's set up some local state on this container component. Where we'll do that is right up here in our constructor. And here we go. I'm saying this.state, and I'm just going to take the course props that are coming in and just use Object.assign so that I can take a copy of that so that I don't end up holding that reference and passing it around. And I'm also going to track errors. For now, errors are just going to be an empty object, but if we do end up adding validation to the form, then tracking the errors in state will be useful right here. And now, of course, my this.state.course call right here is valid. Now if we look back at the CourseForm, we can see that there're some other props that it's expecting here. It's going to require a save and a change function, which we haven't defined yet. We will here in a moment. It's also looking for an array of authors and our errors object. So let's add a few of these in here. We already defined state up above that will handle our errors, so we can go ahead and add that in. So errors will equal this.state.errors. And we can also add allAuthors, but we don't have that data yet. So for now, instead of referencing anything here, I'm just going to put in an empty array, and we'll solve this problem in a bit. But we'll see how to get allAuthors from our API as well so that we can populate the Author drop-down on our ManageCourseForm. And, of course, we need to update our PropTypes validation as well just to keep it happy. Course is an object, and it is required. Alright, this should get us rolling. Let's jump back into the browser and see how this looks so far. There we go. We've got a form displaying. We have a duplicate header, which we can easily take care of. And we can see we have a title. We have an author. Now this, of course, isn't populated yet because we haven't pulled in author data, and our form looks very nice in a very vanilla, lame, oh man, I think I've seen this a thousand times before Bootstrapy kind of way. Hey, I'm not a designer, but this looks just fine to me. Now if we open the console, you can see that we're currently getting a few warnings because we're not passing down all the expected PropTypes. We'll add those in a moment. For now, the biggest issue that we have is the Author drop-down. We know that author data is available via a call to the mock API, but the question is how do we get it into our container component so that we can pass it down? Let's make that happen in the next clip. But before we jump to the next clip, let's quickly take out our duplicate header. I'll remove it right here, and that means we can also remove this wrapper div, and now we have a nice, clean container component. Notice now that our container component has just one tag in it, which is referencing the CourseForm. And if we come back over here, now we just have one header as we'd expect. Alright, now we're all set. Let's move on and work on populating this Author drop-down.
Create Author Actions
We have our CourseForm displaying, but for us to move forward, we need to populate the Author drop-down. This is a great chance to really drill in the use of thunks, actions, and reducers through some repetition. The first step is to fetch author data on initial load from the mock API. Of course, we need an action call. We already have courseActions, but author data is really a separate idea. I could create a loadAuthors thunk over here in courseActions, but that just doesn't feel right. Instead, I'm going to create a new file called authorActions. This file will hold all the actions for dealing with authors. In a full app, you can imagine this containing actions for creating, updating, and deleting authors. For now, our authorActions are going to look very similar to our courseActions. So I'll just paste in the author thunk here. This is pretty much identical in philosophy to the course thunk that we created earlier. It's just that we're dealing with author data now instead of courses. So there's really nothing unique here to cover. You can just notice we're referencing a different API and, of course, have different function names and action types that we're referencing inside this file. We obviously need to create this constant as well, so I can go over to actionTypes. We've got this one, paste it over, and instead of COURSES, we're going to say AUTHORS. And I will copy and paste this left to right. And through the magic of copy/paste and a little bit of manipulation, our action creators for authors are all set. Now we can shift our focus to the authorReducer in the next clip.
Create Author Reducer
Alright, now that we've created our authorActions, we need a corresponding reducer. So let's go ahead and hide the terminal just to get a little extra space here, and we'll go over to the reducers and create our authorReducer. Again, the structure of this is going to be pretty similar to what we did in the courseReducer, so I'm going to paste this over, and it's going to be called the authorReducer. State will start with an array because we're just going to have an array of authors that we're dealing with here. But instead of LOAD_COURSES_SUCCESS, we'll say LOAD_AUTHORS_SUCCESS, and we will be getting the action.authors in this case. Hopefully this repetition is really helping drive home the structure of how we run actions through in a unidirectional way using Redux. Now, as you can see, right now we're hardcoding in an array here, but now that we have two different reducers, this is a good time to think about centralizing our initial state. I like to declare my initial state in a single place, and I typically do that in my reducers folder, although some people prefer to create a constants folder to make that happen. I'm going to do so here in my reducers folder though, and I'm going to call it initialState.js. So this is a file where we will centralize our declarations about what is in state. We'll begin by exporting default an object, and this object will define our initialState. It's going to contain an array of authors and an array of courses because that's what we're holding in our initialState right now. Now, of course, since I've created this, I can go back over here. And instead of saying array right here, I can reference my initialState. This isn't absolutely required. I just find it helpful. Let me import now. I will import initialState from './initialState'. And now right here, when I initialize my reducer, I can say initialState.authors. And we'll want to make this same change over here in our courseReducer. So let me just copy my initialState here, go over to our courseReducer, and do the same thing. And then right here I will call initialState.courses. The reason that this is helpful is as you create more and more reducers, it becomes rather tricky to hold exactly what's in the store in your head, so it's helpful to create a separate piece of initial state that shows somebody okay, this is what our store looks like. Yes, all these reducers are dealing with a slice of this store, but now we have a picture of how our store is initialized. I find that this is really helpful to help me understand the object graph that is being held in our store. And with that done, there's just one extra piece of work for us to do. And since we've created a new reducer, we need to add it to our rootReducer. So let's open our rootReducer, and we'll import our author Reducer and then, of course, add it right down here under our courses. And always be sure to remember this. It's easy to forget, but be sure to add any new reducer that you create to your rootReducer. And with that done, there's just one extra piece of work for us to do. And we'll go back to the entry point of our application. Right now, we're dispatching loadCourses. We also need to dispatch loadAuthors as well now because we're going to want the author data on load. So I will import loadAuthors authorActions right here. And then I'll just copy and paste this as well. We're going to loadCourses and loadAuthors. So that nearly wraps up our Redux flow. Now we just need to open the ManageCourse component and put this data to use.
Map Author Data for Dropdown
Alright, we're almost done working with our author data. Now that we have the author data available in our store, let's update the ManageCourse component to use it. First, we'll expose authors as a prop on our container component by adding it to our mapStateToProps function. But there's a problem here. We can't simply pass the list of authors as is. See, the shape of the author data that's in our store isn't a good fit for placing in the drop-down. If we jump over here to the SelectInput component, we can see the shape of data that it's looking for. It's looking for an object that has a value property and a text property. But the data that's coming down from our store is going to have an author with an ID, a first name, and a last name, so the shape isn't quite right. So what we need to do is translate the shape that came from the API into something useful for populating the drop-down. The place to do such data transformations is the mapStateToProps function. What I'll do is paste in a function here that will transform our state. We'll use the map function and then return a new object here that has the properties that we need, a value property and a text property, value set to the author.id and the text set to the author's first name and last name concatenated together. And then, of course, we'll need to pass that list of authors to our component right here because this list of objects will determine the properties that are bound to our component. Now, of course, we need to jump up here and add it to PropTypes as well, and this will be an array. And we'll also now be able to use it up here on our CourseFrom. We can say this.props.authors. Now I've had the terminal closed as we've been building this, but let's just open it up and check for any issues. And what we can see is something rather odd. Course and authors are missing in props validation, but we can see that they're defined. I had to stare at this a while before I realized the typo that I'd made here. The PropTypes right here should be a lowercase p. It's an easy typo to make, so I wanted to let you see this. And then once I hit Save, we can see that it now works okay. Now if you rewind, remember, I created this from a template, so if you used my template from that previous clip, just keep in mind that it needs to be a lowercase p. I've since fixed my template that I created for this course, but this is an easy typo to make in real life as well, especially since PropTypes used here uses a capital P. Alright, with that set up, let's jump over to the browser and see if this works. There we go! Now our authors are listed in the drop-down as we expected. Success! Now again, that was a lot of moving parts because we just stood up a whole new slice of state, which required us to create a new action file, reducer file, and update our rootReducer and so on. But as you'll see, adding more features for handling courses will now be easier since the structure is in place. Remember, Redux is most useful for larger complex apps. On trivial apps like this, the boilerplate may feel tedious, but the larger the app and the more comfortable that you get with this flow, the more this architecture is going to pay off in scalability, maintainability, and clarity through consistency. So what next? Well, if you tried typing in the form fields, you likely already know our next step. We need to handle changes to our form fields. Let's do that next.
Create Form Change Handler
Alright, we have our form displaying, but the next obvious issue is we can't type in the form fields. I'm hitting keys right now, trust me. So why can't we? That's because they're manage components that we haven't defined a change handler for yet. So let's jump back to the ManageCoursePage and add a change handler. I'm going to use the same pattern that I used in my Flux course here. This single change handler is for all of our form fields. By convention, each form field has a name, and that name allows me to update the corresponding value here in state with a single function. For a closer walkthrough on this, you can check my Flux course. But we also do need to add a bind up here at the top so that we have the proper this context when updateCourseState is called. And now we can come down to our CourseForm and add an onChange handler, this.updateCourseState. And with this change, we should be able to come back over to our form, and there we go. Now we can type in our form field, so that's a good sign. And now that we can finally type in our fields, let's set up the form to actually save our data.
Create Save Course Thunk
Our form needs a save function. That's going to require a new action. So let's jump over to the courseActions file, and we can put our new thunk right here below loadCourses. As you can see, it's called saveCourse, and it just has a couple of differences from the loadCourses thunk up above. First is that I'm forking on our logic here based on whether an ID is passed in for the course. I'll either update a course or create a course. If there's an ID, then obviously I'm updating a course if it already has an ID. One other thing to note is although I'm planning to pass the course into the form right here as a parameter, you can see here that there's an optional parameter called getState you could use. This is useful for cases where you are wanting to access the Redux store and get particular pieces of state out of it right here without having to pass it in as a parameter. In this case, I don't need this feature since I'm getting the data that I need passed in right here, but in larger applications it can be useful to just access the Redux store directly to get pieces of state that you need to work within your thunk. Now as you can see, I'm calling updateCourseSuccess and createCourseSuccess. We need to create those action creators now, so I'll place those just below our loadCoursesSuccess action creator. And, of course, these new constants that we're referencing need to be added to our actionTypes file, so I'll come over here and paste those in as well. We now have a CREATE_COURSE_SUCCESS and UPDATE_COURSE_SUCCESS. Now that our new thunk and action creators are set up, we can shift our focus to the reducer in the next clip.
Handle Creates and Updates in Reducer
Now it's time to update the reducer. We're going to handle both the creation and the update separately. So let's go over to our courseReducer. Right now we just have LOAD_COURSES_SUCCESS in here. First, I'll add a handler for creates. Your first instinct when handling an add is likely just to push another element into an array, so you might be tempted to just say state.push here. But remember, in Redux, state is immutable. Immutability is important for performance, simplicity, and predictability, and it's precisely what enables interesting features like time-travel debugging. So we can't simply push another value into state right here. As a reminder, these three dots are the ES6 spread operator. And what the spread operator does is explode all the values inside the array right here, just as though I had piped out all the values of the array one by one by hand. That's why the spread operator is a pretty good name. It just spreads the values out. In this case, that's really handy because state is immutable. So the spread operator effectively creates a copy of our existing array of courses that are held in state, and then we can include the new course that was just saved within the new array that we're creating. Pretty slick! We just use Object.assign to make sure that we're creating our own copy here rather than attaching to an existing reference. Also, remember, the state variable here represents just an array of courses. That's it. It's a specific slice of our entire store. So this reducer is only handling an array of courses. Now let's add our update. As we can see, the updateReducer looks a little bit different. Again, since state is immutable, we can't simply change the appropriate index in the array. Instead, we need to use the filter function, which is part of ES6, to get a list of all the courses except for the course that's being updated. We slap the spread operator on the front, and that is what creates a brand-new array out of the filtered results that are returned from filter. And then we use Object.assign to create a copy of the course passed in and include it in the array that we're ultimately returning. This likely feels a bit odd and confusing to you at first. Trust me, after you work in immutable data for a bit, you'll get very comfortable with these patterns and tools. You'll find that you often use the spread operator, map, filter, and Object.assign to get things done. Once you understand those four tools, you can accomplish really powerful tasks without mutating state. Alright, now that we've wired up our actions and updated our reducer, we need to update the ManageCoursePage to put all this to use. One thing to note though. Did you notice how much quicker this went now that our infrastructure is all set up? See, things are getting easier.
Dispatch Create and Update
Okay, we're back in the ManageCourse component. Now that we have the actions that we need, let's create a saveCourse function. It will simply dispatch the action that we just created. I'm going to add it right here above our render function. And as you can see, we're calling saveCourse, which is on actions, and that should be available since we're already passing it in on props. And I'll add the corresponding bind up here within the constructor. And finally, we just need to come down to our CourseForm and pass in the onSave prop, and we'll pass it a reference to the saveCourse function that we just defined. One other piece we should add is some PropTypes validation because we are using actions, but we haven't added that to our PropTypes validation yet. So this specifies that we're expecting actions to be passed in. Great! Now we should be all set to create new courses now, so let's add an addCourse button to the CoursesPage. So let's navigate over to the CoursesPage. So let's add a button to the page right here under the header, and this will be our Add Course button. As you can see, I am just calling a function redirectToAddCoursePage and adding a little class here just to make some nice Bootstrap styling. Of course, we don't have this function yet, so let's add that. I'll just paste it right up here above our render. And again, you could use an arrow function here and just put this in the arrow function if you prefer. I'm just doing this because it's a best practice for performance. And finally, we need to add a bind up here within our constructor. Bam! That should do the trick. And if it doesn't, I'm going to throw my mouse across the room. I mean come on! We just wrote a lot of code together. I really can't afford public failure at this point. So let's jump over to the browser and see if I get to keep my job. Alright, I will click on a link here, and let's just throw some random data in. Select this and hit Save. And nothing happened. Nothing at all happened. Now that shouldn't necessarily surprise you because remember we haven't really wired up anything that should happen once we hit Save other than making a save behind the scenes. We can see we aren't getting any errors. Now if I click on Courses though, we can see that the course that I just entered was written successfully, and the author was assigned to it as well. So we do have success. Not a great experience yet, but we are now creating courses. So awesome! Looks like I get to stay being an author for now at least, assuming you didn't stop watching this 30 minutes ago. Of course, this is still pretty clunky, so on the next clip, let's redirect upon save.
Redirect via React Router Context
Our save works great, but we should redirect the user back to the list of courses when the save is complete. Let's use React Router to make that happen. In this case, I'm going to show a different way to get it done. This time we're going to use React Router's context to redirect. To set up React Router's context, you have to declare that you want it, and we can do that by declaring the contextTypes that we'd like to import on our component. Since contextTypes is a static property, it has to be done after the class definition, so a logical place to make this happen is right down here by our PropTypes. And I'll just paste this in so we can see how it works. So again, we're referencing our class, but this time we're referencing the contextTypes that we'd expect, and we're effectively saying that we want router to be one of the contextTypes that are required. And by doing this, this makes React Router's context available to us throughout this component. If you're not already familiar with context, it's basically a global variable that library authors use, but that we, as library consumers, should avoid. Yes, global state is generally evil, but context is used by both React Router and Redux in some places to provide easy access to the data that we need without having to write boilerplate plumbing code. And that's exactly what we're avoiding here. And now that we have access to the router's context, we can go back up to our save function and put it to use. After we call saveCourse, I'll just call this.context.router, and we will push a new item to the router on our context object. So this will change our URL to /courses. Alright, let's save this and try it out. We'll come back over and hit Add Course. I'll just put in some gibberish and hit Save. And when we do, there we go! It came right over, added it in. Our redirect worked quite nicely. As you saw, it's still a little bit clunky. We can see the new record pop in here, so there's some room for improvement, but what we have here is generally working. But first, we have a bigger problem. If you click on an existing course, you'll notice that it's not populating that form, so we need to figure out how to handle updates when working with Redux. Let's fix that in the next clip using mapStateToProps.
Populate Form via mapStateToProps
Update State via componentWillReceiveProps
We have our courses form populating in some cases, but as we just saw, it still doesn't populate when we load the page directly. Why? Well because when the props change, we need to update our container component's state. To do that, we'll add some code in componentWillReceiveProps. Let's place this function right below the constructor. This React lifecycle function is called anytime that props have changed, as well as anytime that React thinks that props might have changed. This last sentence is an important distinction, so let me repeat it. This function may run sometimes even when props haven't changed. That's because sometimes React can't tell for sure if props have changed, so it runs this function for safety. And that's exactly why I'm doing this check here to say has the course's ID changed? And if it hasn't changed, then don't run this part of the function because my whole goal here is to make sure that if we end up getting a new course on props, then that's when we need to update state. But I can't let this run all the time, or it would end up overriding our state. We only want to update state with our props when we have ended up requesting a new course. And with this change in, now we should have a fully functioning form. I should be able to refresh this form now. And even though I'm referencing it directly, we can see that it populates successfully. Now we can still navigate around, hit the different courses along the way. I should be able to come in here and change the name of any of these courses and hit Save and see it reflected right here on the page. Very nice! So in the final clip for this module, let's wrap up all that we've discussed and review where we are so far.
In this module, we enhanced our application by creating a dedicated page for adding and editing courses. We saw how to create reusable form components that centralize and abstract away the complexity of dealing with Bootstrap's expected markup structure and CSS classes. And we populated the form on page load using a combination of mapStateToProps to initially populate the form when mounted and componentWillReceiveProps to update local state when the props change. In the next module, let's polish the application's user experience further. We'll create some new dedicated actions and reducers so that we can properly manage and display the status of async calls when using Redux.
Async Status and Error Handling
Welcome back! Our application is coming together nicely, but there's still a lot of room for improvement. We're completely ignoring async status and error handling. So in this module, let's use React, Redux, promises, and a few other tricks to polish our application's user experience. This short module is all code, so we're going to jump right back into the editor and continue coding. Here's some specific issues that we want to address with the current user experience. First, when the app first loads on the Courses page, you see a blank white screen until the API call to retrieve course data is complete. Our form experience is poor too. When the user hits Save, we have no indication that an async save is in progress. Instead, we're being redirected to the Course page before our save is complete. So we're seeing the updates plop in after our redirect, which is rather clunky. Finally, if the API call fails for some reason, it currently does so silently because we're not properly handling rejected promises. So in this module, let's jump back into the editor and address these key issues. The first is to start tracking and displaying the status of asynchronous calls like AJAX calls to the server, and the second is to elegantly address errors that may occur when making API calls in our thunks. That's the plan. Alright, let's get to it!
Create Preloader Component
Our app doesn't currently tell the user when an async call is in progress, so let's solve that. Rather than blocking the entire UI, I'm going to do something subtle. I'm going to use a component that simply displays a number of moving dots. Let's create a new component in the common folder, and we'll call this file LoadingDots.js. I'm just going to paste it in. I'm not going to walk through and detail how this works. But I will say credit for this component goes to Ryan Florence who tweeted about it a while back. I admire the simplicity. In short, what it does is displays a list of dots, and it's a configurable list of dots that you can provide on props, and then those dots are displayed at a given interval, which is configurable. You can pass in props to configure how fast these dots display on the screen and how many dots display on the screen as well. And to display this, let's go over to the Header, and we'll add an import for our new component. I'll also add a link to it right down here. We'll just display it right after our about link. And I'll hit Save on that. You can see that I'm saying that every 100 ms it should show a new dot, and it should show up to 20 dots. Again, this is all configurable. And I already have my console running here, so we can see that it's looking good so far. Let's just jump over to the browser, and we can see that this is displaying just fine. We're getting 20 dots, and it just puts those in one at a time. So this is sort of a subtle preloader. Rather than pulling in an image, we're just animating in the display of dots. This is how we'll convey to our users that an AJAX call is in progress. Of course, the obvious next step is to set this up to only display when an AJAX call is happening, so let's get that started in the next clip.
Create AJAX Status Actions
Now that we have a preloader to display when AJAX calls are in progress, we need to keep track of when they begin and end. So to make that happen, let's create a new actions file called ajaxStatusActions. This file will contain actions for tracking the status of our AJAX calls. Of course, the most obvious action will be beginAjaxCall, so let's create that. As you can see, I'm referencing my actionTypes file and then creating an action creator called BEGIN_AJAX_CALL. Of course, we haven't created this action type yet, so let's come over here, and I'll just place it here at the top. So that should take care of the action side of things. Now let's shift our focus over to the initial state of the application. I'll open up the initialState file, and let's just update the initial state of our application to track the number of AJAX calls in progress. So I'll call this numAjaxCallsInProgress because remember we could have more than one in progress at any given time. So now that we have our initial state set up, in the next clip, we'll shift our focus to creating the ajaxStatusReducer.
Create AJAX Status Reducer
Now we're ready to create our ajaxStatusReducer, so I'll open a new file and call it ajaxStatusReducer. There're going to be a few unique aspects to this reducer, so let me paste in the basic structure, and then we can discuss it. I'll go ahead and close my navigation so we can see the whole thing. As you can see, here I'm using an if statement instead of a switch. For simpler reducers like this, I find an if statement preferable. It will also help me pull off a little trick that I'll show you in a moment. Now, as you can see, anytime an AJAX call begins, I simply increment the state by 1. The state in this case is the ajaxCallsInProgress counter that we just added to initialState. Remember, each of our reducers is just working with a small slice of state. So this keeps track of when AJAX calls begin, but how do we know when they end? For that, I'm going to use a trick that Dan Abramov, the creator of Redux, suggested to me when reviewing this demo. I'll use a convention. Remember how all our thunks ultimately dispatch a success action when they complete. Well that means that I can use the SUCCESS suffix as a signal that the action is completed. Brilliant! This will help me avoid manually dispatching a separate end to AJAX call action every time an AJAX call is completed. To do this, let's add an else statement that checks to see if the action type ends in SUCCESS. As you can see, I'm calling a function called actionTypeEndsInSuccess, so let's create that. I'll just place it right above our reducer. You could do this in a number of different ways. I could refactor this out to a separate business logic file in some separate part of the system, but this is fine for now. What you can see I'm doing is just using the substring function to be able to get the end of our action type and see whether it ends in SUCCESS. Now before we move on, I want to note something else important. It's important to note that we're now handling the same action in multiple reducers. Any action type that ends in SUCCESS will now be handled here, as well as in another reducer. And there's nothing wrong with this. In fact, it's quite powerful. Remember, each reducer is simply a slice of state, so a given action may impact multiple reducers. And since we've created a new reducer, we need to add the reference to our rootReducer now as well. Don't forget this. It's an easy thing to forget, and if you do, it can really confuse you and make you wonder why your reducer isn't getting called. So let's now add the reference to our rootReducer. I'll just import it up here at the top and then add it right here. Great! So we have our action set up, our initialState, our reducer set up. So now the next step is to go update our courseActions thunk so that it puts all this to use.
Call Begin AJAX in Thunks
Okay, now that we've created our new action and reducer, we just need to update our existing thunks to call it, so we'll need to open up courseActions and authorActions so that we can update them to play along. Let's begin with courseActions. I'll add an import to beginAjaxCall, the action creator that we created earlier. And now we just need to make a dispatch down here in our loadCourses and saveCourses thunks. So I will do that at the very beginning of the thunk so that we dispatch this action creator, beginAjaxCall. We'll do the same thing right down here in saveCourse. We'll need to do this as well over in our authorActions since it also has a thunk inside. As the first part here, I will dispatch beginAjaxCall. Now you might be thinking wouldn't our mock API be the right place to do this? Then we wouldn't have to remember to add this dispatch call to every thunk. And you're right. That's true. That's certainly something that I would recommend considering in a real app. That way it's handled in a centralized way, and people don't have to remember to dispatch beginAjaxCall in their thunk. However, I wanted to avoid adding code to our mock API to avoid creating confusion, and also, there's another advantage to my current approach, and that is that I can decide to not show a preloader for some thunks. For instance, maybe I want to do an optimistic update or delete. In other words, I want to immediately update the UI when someone clicks a button without waiting for the response to return. Such a technique makes UIs feels extremely responsive because they always respond instantly no matter the speed of the API call. So, for example, imagine that I had a deleteCourse thunk in here. I might want to immediately dispatch the DELETE_COURSE action so that the course was immediately taken off of the page even though the AJAX call was still in progress. That's what I mean by an optimistic update. And in that case, I wouldn't want to dispatch beginAjaxCall because I'd just want the AJAX call to quietly happen behind the scenes. Alright, we've wired it all up, so let's check out the browser. Hmm, as we can see, the preloader is still displaying all the time. Ah-ha, well that's because we haven't added any code that actually hides it based on status, so let's do that final piece now.
Hide Preloader Based on Status
We now have all the infrastructure in place for properly tracking AJAX call statuses in our Redux store, so let's put this new information to use. First, we need to open up our top-level component. That's our App component. As you can see, it's not currently a connected component because we didn't need to connect the Redux store or pass any actions down on props. But now we're going to need to connect to the Redux store so that we can get the loading status and pass it down to the Header right here. So let's add a mapStateToProps function. As you can see, I am just getting the number of AJAX calls in progress from state and then converting that into a Boolean, effectively saying that we will assume that something is loading if the number of AJAX calls in progress is greater than 0. And now we can update the call to our header component to put this new information to use. Of course, we need to add a reference to connect now that this is a connected component, and we need to update our call down here to put connect to use. Now we will pass mapStateToProps in and, of course, reference our App component. Now let's step over to the Header component so that we can update it to use this new loading data. I prefer to destructure props that are passed in. So in this case, we're expecting loading to be passed in. And now I can use that information to determine whether loading dots should display. Now I'm going to use a syntax that I really enjoy, but it might look odd to you at first. I'm going to say loading && and then put in our reference here to our component. I really like how terse this is. This technique leans on the short-circuiting nature of the logical and operator. The right-hand side over here will only evaluate if this left-hand side is true. So the loading dots component will only display if loading is true. Nice! Now I could use a ternary if I want here, but I really feel like this is a nice, clean way to get it done. The one other piece we need to add is some prop validation because we are expecting a loading prop here now, and we need to also update our prop validation over here on App.js. Now with that complete, we should be all set to jump back to the browser and see our work in action. But we've got a problem. Looks like my initial state on my ajaxCallsInProgress reducer. Let's take a look at this, ajaxCallsInProgress. So that looks okay. Let's look at our initialState. It is numAjaxCallsInProgress right here. Ah, but here I called it ajaxCallsInProgress. So let's go over to my initialState, and we'll just rename this to ajaxCallsInProgress. And that should make it less cranky. Let's try reloading here. There we go. And as you can see, up here at the top, when I hit Refresh, we can see our preloader show for a moment until the course data and author data has come down. And this is easier to see even here because we will see the preloader shows until the data comes in. So great! That worked out quite nicely. And now that we know when an AJAX call is in progress, we can put this information to use to improve the user experience in other places. But for now, let's turn our focus to the Save button and see how we can improve the user experience when editing and saving records on the Manage Course page.
Use Promises to Wait for Thunks
We're now displaying the AJAX status so the user has a better idea of what the application is doing. Let's continue improving our app by enhancing the behavior of our Manage Course's page. Right now, when you click Save, it immediately takes you to the course list page. That's a problem because it's showing us stale data that doesn't yet include the new changes that we just saved. We can see the updated record display shortly after the redirect completes. And the slower our API is, the uglier that this problem looks. Thankfully, this is easy to solve. Instead of redirecting immediately when the user hits Save, we need to wait until the API call is completed. Since thunks utilize promises, this will be pretty easy to pull off. We can just use a .then to specify a function that we'd like to call when the promise resolves. So let's go back to our ManageCoursePage function, and in saveCourse, what we can see is right now we call the saveCourse action and immediately go back to the courses page. Instead, let's go ahead and use .then so that it's delayed. This function right here, redirect, won't be called until this promise has resolved. So let's refactor a bit. We'll take this out of here and place it in a new function called redirect, and now this function won't get called until the saveCourse promise is resolved. Let's give this a shot. So now I should be able to come over here, change some data, hit Save, and we wait. There we go! And now we aren't taken back to this page until the AJAX call is actually completed. The promise-base nature of thunks makes waiting for them to complete quite simple. Of course, the obvious problem is we're not really providing good feedback to the user when the Save button is clicked. It would be nice if it responded by disabling the button and showing an indicator so that we know that processing is occurring when the API is slow. So let's make that happen in the next clip.
Create Form Submission Loading Indicator
As you can see, right now when I click Save I get no feedback at all until we get redirected. It would be nice if it would at least acknowledge the click and disable the submit button so that we knew that it was working. This is easy to pull off with a little state, so let's do that. Now as a side note, one of the common questions in Redux is when to use local state. In this case, local state is useful because this is fleeting data that the rest of the app will not care about. I could run this interaction through the Redux flow, but in this case, I feel it would just be unnecessary overhead. So instead, let's just add a field right here to state, and we'll call it saving and initially set it to false. Now we can come down to our saveCourse function, and right after we preventDefault, the next thing we'll want to do is set saving to true. And we can toggle this back to false within our redirect call because we know that this gets called after the save is complete. We should also pass this down to our CourseForm so that this information is available where we need it because we want to change the behavior of that particular button. So now the data that we need is getting passed down to the CourseForm. Let's go ahead and open it up. As you can see, I have it say Saving when loading is true, and otherwise, it will just show the default, which is Save. So we don't actually need to make any changes here. We're all set. It's nice how easy it is to reuse our data now that it's available. Let's check this out in the browser. So now I can come in, hit Save. We didn't see any changes. And I had to look at this for a moment, but I realize the mistake I made was over here in ManageCoursePage I'm sending this down as saving. It's a Boolean that determines whether it's currently saving. But when we initially set up CourseForm, I called it loading here. So what we need to do is change loading to saving, and we need to change it here, here, here, and then also up here. And that should be the four spots. Up here is one of our props, down here we're using it to determine whether it should say Saving or Save, and then, of course, down here in our propTypes as well. So now that that's saved, let's give this another shot. Come over here and refresh the app, and it loads up fine. And now when I hit Save, there we go. Now we get a much better experience. Now when I come in to edit one of my records, it disables it, shows a little icon, and now we get the visual feedback that we need to feel assured that our save is in progress. Now our form is starting to feel polished, but it would also be nice if we notified the user when the action is complete when we land back on this page, so in the next clip, let's add a toast.
Display Save Notification
Our app is coming along nicely, but we need a friendly way to tell the user when actions are successfully completed. For that, I'll use the same notification library that I used in my Flux course called toastr. It's already installed since it was listed in our package.json in the beginning of the course. So as long as you used the starter kit, you should be all set to just go ahead and import it on the page. The place that we want to import it is on the ManageCoursePage here at the top. Now toastr also comes bundled with some CSS that we'll need, so for this first time that we're using it, we need to go over to our application's entry point and add this in. So let's go over to index.js, and I will add an import for toastr's minified CSS right here. So now we're set to go back to the ManageCoursePage, and we can go down here to the point where we're doing our redirect and add a little toast to notify the user. I'll just add it here on this second line, and I'll say toastr.success, and then we can put our message right here. And our message is that the course was just saved. That was easy enough. Let's go to the browser and see how it looks. Go ahead and open one of these, hit Save, we see this notification, and there we go. Now we've got a nice toast that pops up anytime that we come in and edit one of our courses. So what we've done now covers the happy path, but in the next clip, let's set up some error handling for when things don't quite go as planned.
Great! Our form is looking good, but so far we've totally ignored error handling. So what happens when our API throws an error? There's an easy way for us to find out. The mock API that we're using throws an error if we provide a course that has too short of a title. If we look right in here, I'm checking for a minCourseTitleLength. So effectively, I'm simulating server-side validation here and then rejecting the promise if the course title is less than the minimum course title length. So let's go over here and click Add Course, and I'll just immediately hit Save. You can see that we're just stuck. After I hit Save, preloader keeps going. It never stops. I can go to Inspect, and when I inspect, what I can see is an unhandled promise rejection Title must be at least 1 characters. Chrome is doing us a nice favor here. Until recently, all browsers would just quietly swallow unhandled rejected promises, so it's nice that we're at least seeing something here. But obviously, this is not a good user experience. So what we need to do is handle the rejected promise in a more elegant way. So let's go over to courseActions and look at the saveCourse thunk. And what we can see is we're just simply throwing the error. There's at least two ways that we can handle this. The first is that we could dispatch a saveCourse error action right here and pass it the error message that we've received from our API call, or we can simply handle the rejected promise at the call site, which in this case is the ManageCoursePage. In this case, let's go with that approach. So we'll go over to the ManageCoursePage and look at our saveCourse call. And you can see right here that we don't catch any errors. So what we can do is add a .catch right here so that we can handle the error that we receive. We'll add in an arrow function, and then here in the body, we can add any error handling code that we need. How about we do something a little more friendly. We'll use toastr now that we have it handy, and I'll say toastr.error, so this will just give me a different color of toast, and we'll pass it the error message. With that added in, now we should see a toastr error. But there's one other thing that we need to make sure to catch, which is that now that our AJAX call has failed, we also need to be sure to setState for saving back to false. So I'm just going to copy this line here because once the AJAX call has failed, we need to make sure to update our state because we are no longer saving at that point. It's an easy thing to forget. But now that I've hit Save, I should be able to come back over here. And when I hit Save, we can see our toast displays right up here, Title must be at least 1 characters. So great! That looks nice. We get a good message for the user, and we're no longer getting the unhandled promise error that we were seeing earlier within Chrome. So that's good news. However, if you're looking closely, we have one issue left. See how the preloader's continuing to display. The problem is our Redux store state isn't being updated to reflect the completion of the AJAX call. That's why the preloader image is just continuing to display. The fix for this is simple. We just need to dispatch an action when an AJAX call errors out. So let's open up our actionTypes file, and we'll add a new action here. We'll call it AJAX_CALL_ERROR. And then we can go to our ajaxStatusActions file and add a corresponding action creator called ajaxCallError. And as you can see, it just uses the action type that we just defined. Now that we've set these up, we can go over to our ajaxStatusReducer and put this to use. As you can see on line 12, currently we are reducing the number of AJAX calls in progress anytime that an action type ends in success. But now we have a new way that we might want to reduce the number of AJAX calls in progress, and that new reason is if the action's type is equal to AJAX_CALL_ERROR. So let's read this and make sure this is clear. Now anytime that we get an AJAX_CALL_ERROR or an actionTypeEndsInSuccess, we're going to reduce the number of AJAX calls that are currently in progress because in either case, that AJAX call is now completed. It either completed with success or it completed with an error. And now that we've updated our actions and our reducers to handle AJAX call errors, we just need to go update our thunk to dispatch this action when an error occurs. So let's go back over here to our courseActions, and here's our saveCourse thunk. And what we'd like to do down here, instead of merely throwing the error, we would like to dispatch an ajaxCallError anytime an error occurs. Now we haven't added a reference to ajaxCallError yet. We just need to add it as a named import right up here. And that should do the trick. Now when an error occurs, our AJAX status should get updated to reflect the completed call. So let's give it a shot. Come back over here, I'll hit Save, we saw the toast as expected, and now our preloader disappeared after it errored out. And we do see the preloader does show for a moment, but the minute that the error occurs, preloader is taken away because it's now being handled properly in our reducer. So we've definitely polished the user's experience with a lot of little tweaks here around handling asynchronous statuses and errors properly. Let's wrap up this module with a short summary.
Alright, our app is in great shape. We're now properly displaying and tracking async status, and we're handling errors in a more elegant way by displaying them to the user via toastr and updating the status of our AJAX calls so that our preloader is hidden as expected after an error occurs. Sure, there're still a lot of interesting opportunities for improvement in our app, and I'll address those with some specific challenges to you at the end of the course. But there're two large topics that I've saved to wrap up the course, testing and production builds. So in the next module, let's explore how to create automated tests for both React and Redux.
Usually, testing the front end is hard. With React, life has gotten significantly simpler. React is built on the idea that components produce HTML based on props and state. This simple design makes testing React components quite straightforward compared to many alternative UI designs. Here's the plan. If you're new to automated testing, just deciding what to use can be a major hurdle, so we'll begin by reviewing the long list of options to consider in this space, including testing frameworks, assertion libraries, and helper libraries. Once we've clarified our testing stack, we'll spend the rest of our time back in the editor writing tests. We'll test the same component using both ReactTestUtils and Enzyme in order to contrast the benefits of each approach. As you'll see, we can simulate a render without opening a real browser, and we can pass properties to our component, simulate interactions, and assert on the results of these interactions. Now this is a big topic with a lot of ground to cover, so I'm just going to show one way to get it done with my preferred tooling. But know this. There're many other options out there if you don't like what you see here.
Where to Test
Testing React with React Test Utils
Let's begin with the easy stuff. We'll write some tests for React presentation components first. As we discussed in previous modules, the vast majority of the components in our app should be dumb presentation components. They should simply take some props and return some markup. This makes testing clean and simple. We can just assert that given certain props we receive the expected output. Now when testing React components, we have to choose between shallow rendering and rendering to an actual DOM. ShallowRender is faster than rendering to a real DOM. Why? Because shallow rendering tests a single component in isolation. Child components aren't rendered. However, there are some limitations with shallow rendering. At the time of recording, shallow rendering doesn't support refs and testing interactions. These limitations may be solved in the future since Facebook is actively working to improve shallow rendering, and it also considers shallow rendering the recommended approach in the future. So we'll use shallow rendering to test our presentation components. But in an upcoming clip, we'll need to test interactions between parent and child components, so we'll use the slower render into document style at that time. And to clarify, since we're using Enzyme, Enzyme labels renderIntoDocument as mount. But understand that behind the scenes, Enzyme will be calling ReactTestUtils and using its renderIntoDocument function because, again, Enzyme is really just an abstraction over ReactTestUtils. Let's use shallow rendering to test the same React component with two different technologies. First, we'll use ReactTestUtils, and then we'll write the same test in Enzyme for comparison. Before we get started testing, I'd like to change a setting on Mocha's test reporter. Right now it's set to progress, but I'm going to change this to spec. I normally like to keep it set to progress when I'm doing development because that really helps minimize the noise that's written to the console, but during our initial creation of tests, I think you'll find the spec reporter more helpful because we'll be able to see details about the specific tests that we've written. And with that all set up, we're ready to write our first test. First, let's add a test for the CourseForm component. So I'll come over here to the course folder, and here's our CourseForm, and I'll just add a new file, and I'll call it CourseForm.test.js. We'll begin by importing the libraries that we need. We're going to need expect, which is our assertion library. We'll, of course, need React. We're going to use ReactTestUtils, which I'd already mentioned, which is available under react-addons-test-utils. And, finally, our system under test, in this case, will be the CourseForm. Now let's add our describe block. If you're not familiar with Mocha, the describe block is used to group and label your test so you can nest multiple tests inside. For our first test, we're going to test that the structure of our component is what we expect. Normally, I'd just put the name of the file under test right here, but I'm going to call this group of tests CourseForm via React Test Utils because we're going to create these same tests with Enzyme in a moment so you can see the contrast between the two approaches. And I'm using an arrow function here, but you can also, of course, use an anonymous function if you prefer. For our first test, let's assert that a form and an h1 tag are rendered within our form. So I'll use the it function, which allows me to describe what I'm trying to test, which in this case is that it renders a form and an h1. So I am naming my test. And to get started with ReactTestUtils, it's helpful to create a setup function that will return us the output of rendering the component under test. So let's create a function called setup up above right here. And the first step is to create an instance of the ReactTestUtils renderer. And now we can render the CourseForm component. Finally, to get the output of this render, we need to call getRenderedOutput. Now we're calling CourseForm without any props. We need to pass some props in. I could just add them here inline, but instead, I'm going to declare them up here above for readability. And I'll just paste in the object. And now I can just update the CourseForm to put these props to use. And I'll use the spread operator to make that happen. Remember, the spread operator works on objects too, so this syntax is equivalent to me typing out the props one by one. Quite handy. And finally, let's return a few items from this function that might be useful in our tests. So this way we'll be able to get an object back that contains the props that we set, the output, and the render in case we need it. And that's it. Now that we have our setup function, it'll be far less painful to work with ReactTestUtils, so let's shift our focus back to writing our first test. To begin, let's call the setup function that we just created and get a reference to the output property that's being returned. So this will be the output of us rendering the CourseForm. And now that we have our hands on the output of rendering our CourseForm, we can make assertions on it. First, let's write an assertion that the top-level tag that we're receiving is form because if we go look at our CourseForm, we can see that the top-level tag is form. So I'll open up the command line now and run npm run test:watch. So this will run our tests and watch them. This way anytime that we hit Save it should rerun our tests. And we can see we have two tests running here, our first test, which we wrote in the environment setup module at the beginning of this course, and our CourseForm via React Test Utils. We can see that that's passing as well. So our first test is passing. So we know that we've got our hands on our CourseForm and that the form tag is the top-level tag that we're receiving because the type is form. And now we can get a reference to child elements using output.props.children. So here I'm going to destructure the array that's returned from children and say that the first element is an h1. We're expecting that first element to be an h1 because if I go over to the CourseForm and I look at the children under form, the first child is h1. Of course, the second child would be a TextInput, the third child would be a SelectInput, but I just am interested here in the h1 for this test. So this is just saying take that first element and give it a variable name of h1. Again, let's make sure that we have what we expect here, so we'll write another expect statement that expects the type of h1 to be h1. Hit Save here, and we can see, again, that our test is still passing. So great! We've completed our first test using ReactTestUtils. Let's add a couple more tests. This is obviously a pretty trivial test. Let's do something that's a little more interesting. Back over in the CourseForm, if we scroll down to the bottom, remember this Save button, and we wanted to change the value of that button depending on whether saving was true or not. When it was saving, we wanted it to say Saving; otherwise, we wanted it to say Save. So let's write a test that asserts that this is working as expected. So let me just paste in this next test, and we can talk about it. What we have here is a test that says the save button is labelled Save when not saving because we expect it to say Save when we're not saving. What we do is we call setup again, we get our hands-on output, and then this time we're going to get the fifth child so that we know the fifth child is a Submit button. And the reason that it is the fifth in the index here, we can come through here and say 0, 1, 2, 3, 4, 5. So it is an index of 5. And then what we do is write in an expect statement based on the value of that Submit button. We expect the value to be Save because right now what we did was called setup with a parameter of false. Now if we look at our setup function, right now we can see that saving is hardcoded to false. So what we want to do is actually take a parameter up here on setup, and we will call this parameter saving. So effectively, now we're adding a little bit of power to our setup functions so that we can change the props that we're passing down to our CourseForm. So now we've updated our setup functions so that we can pass a parameter into it. And when I hit Save here, we can see now that our test is passing, that the Save button is labeled Save when not saving. Now let's test the mirror image of this. Here, what we want to say is the opposite. We want to expect it to say Saving... when it is saving. So you can see we're calling the setup function, but this time we're passing true instead of false, so that will end up setting saving to true. Otherwise, this code is the same. We're just asserting now that it should say Saving... instead. And when I hit Save, we're in good shape. We can see that test passes as well. Alright, so now we've seen a basic example of working with ReactTestUtils. Let's build the same test in Enzyme for comparison.
Testing React with Enzyme
We just saw that a lot of boilerplate was required to write a few simple tests with ReactTestUtils, so in this clip, let's use Enzyme to write those same exact tests and see how much simpler it is. Now before we move on, let's rename our ReactTestUtils test so that it's clearly separate from the Enzyme test that we're about to create. And I'll just call it CourseForm.ReactTestUtils.test.js. Now let's create our Enzyme test file, and we'll call it CourseForm.Enzyme.test.js. To get started, let's add our imports. Of course, I'm importing Enzyme here. As you can see, I'm using named imports for the mount and shallow function that are part of Enzyme. For now, we're just going to use shallow. This is Enzyme's wrapper over ReactTestUtils shallow renderer. Now for convenience, I'm still going to create a setup function up here at the top, but this time our setup function is far simpler. As you can see, we're still defining our props, and then we just call shallow and pass it the component that we'd like to render, which in this case we're going to shallow render the CourseForm with the props that we defined up here above. So this is nice and simple. None of the other setup boilerplate that we did in our ReactTestUtils test is necessary. Now let's duplicate the first test, which remember, it simply checked for the existence of the expected form tag and h1. So I really like how this reads. I'm just getting the wrapper component back from our setup call and then using the find function to get the form. And then I can say .length and chain my calls together just like I would in jQuery and say that I expect the length to be 1, that I expect it to find a form and only find one form. And then I can also look for an h1 and get the text of that h1 and expect that to equal Manage Course. This reads much more like English. And now let's add the test for the Save button and see how they compare. Again, the first line is really just the same boilerplate as the first one so that we can get a reference to our wrapper. And then we put our assertion on this second line. We find the input. We want to get the props of that input, and the prop that we're looking for is the value, so we're finding the input, looking at the props, getting the value, and then expecting that to be Save. It should be Save because we are passing false for saving. When saving is false, we expect it to be Save. When saving is true, then we expect the value to be Saving... And if we hit Save, we can see that our test doesn't run. And the reason that it doesn't run is our watch doesn't find new files. So I actually have to hit Ctrl+C down here, and then I just need to rerun so that it picks up this new file that we've created. We'll just have to remember to do this anytime that we create new test files. Now we can see we have seven passing, and our tests are just sitting here at the root. So the one thing that I did forget was to wrap this in a describe block, so let's go ahead and do that now. There we go, and now it displays much more nicely. The CourseForm via Enzyme, here're my three tests, and the CourseForm tested via React Test Utils sits right here. So you can see how the describe block helps me lay out the test results in a more attractive manner. Now let's look at these two side by side for comparison. To me, Enzyme is the clear winner, much less code, and the code that's there is more readable. It clearly conveys intent. It's also less fragile. Note that I had to look for the fifth index in the array to get a reference to the Submit button when using plain ReactTestUtils. But with Enzyme, I was able to just use the find function to get it. That's less fragile.
In this module, we reviewed the long list of testing frameworks to choose from, but ultimately used Mocha, expect, ReactTestUtils, and Enzyme to test. We saw the many potential approaches for testing React components including in-browser via headless browser or in-memory. We ran in-memory test using jsdom's virtual DOM, and we ran them via Node. And we wrote tests for a React presentation component using both ReactTestUtils and Enzyme. We saw how Enzyme provided a superior API for interacting with the DOM. And now that we've seen how to test simple React presentation components, in the next module, we'll wrap up our testing coverage by exploring how to write unit and integration tests for Redux.
We just saw how React's design lends itself very nicely to testing, and as you're about to see, Redux was designed with testing in mind from the beginning as well. In this module, we'll focus on how to test all the pieces of Redux. We'll write tests for React components that are connected to Redux and all the discrete pieces of Redux including action creators, thunks, reducers, and the Redux store, which, as you'll see, is really just a final integration test. We'll continue to use Mocha and expect to get all this done. Let's begin by writing a test for a connected component.
Testing Connected React Components
Let's continue exploring testing with a more challenging problem. How do we test connected components? When testing React components, there are two core concerns to consider. First, we can test the component's markup. Given a certain set of props, do we get the expected output? For container components, the service area should be minimal here. Remember, markup belongs in presentation components, so ideally the only JSX in a container component is a reference to a child component. Second, we can test the behavior. Given a click, scroll, drag, change, what happens? Do we get the expected behavior? We've already seen some examples of testing the markup, so for testing connected components, let's focus on testing behavior. The tricky thing about testing container components is they're all wrapped in a call to connect, and the connect function assumes that our app is ultimately wrapped in a Provider component, so our container components don't export the component we wrote. Instead, they export the component wrapped in a call to connect. Now there's two ways to handle this. First, you can wrap the container component with react-redux's Provider component within your test. So with this approach, you actually reference the store, pass it to the Provider, and compose your component under test inside. The advantage of this approach is you can actually create a custom store for the test. So this approach is useful if you want to test the Redux-related portions of your component. But perhaps you don't care about testing the Redux-related pieces. If you're merely interested in testing the component's rendering and local state-related behaviors, you can simply add a named export for the unconnected plain version of your component. And as you're about to see, if you forget to do one of these two things when trying to test a container component, you'll at least get a handy error message that guides you in this direction. Alright, let's test a connected component. Testing a connected React component can be a little tricky at first. Let's try testing the ManageCoursePage, so we'll create ManageCoursePage.test.js. Let's begin by adding the necessary imports. These are the same imports we used in the previous tests, and let's also add an import for the component that we want to test, which is ManageCoursePage. Now, as you'll see in a moment, this is going to cause us some problems. Let's write a basic test so you can see what I mean. Let's assume that we're going to add a simple new feature to our application in a TDD, or test-driven development, style. We'll write the test first, see if it fails, and then add the feature to turn the test green. And we won't even have to open the browser to know that this feature is working. Let's say we want our form to set an error message if someone leaves the title field blank when they hit Save. Here's the basic shell of a test that we might write, that we're testing the Manage Course Page, and we're testing that it sets an error message when trying to save an empty title. Now we can use Enzyme to create an instance of the component that we want to test. Note that I'm using mount here rather than shallow. Remember, shallow is called shallow for a reason. It only renders one layer deep. Here, we need to test this component's interactions with its child components. More specifically, the title input that sits in the CourseForm component. So, we need to use mount so that a full DOM is created in memory. Remember, behind the scenes, Enzyme is using jsdom to create a virtual in-memory DOM. Now let's try running our test. Now since we've created a new test, we'll need to hit Ctrl+C to stop our testing and then restart it again; otherwise, it will not see that new test we've created. And it looks like we have an error. So we're presented with our first roadblock here. Since our component is exported as a connected component, if we merely try using it directly, we get this error. Thankfully, this error is quite helpful. So we have two options. We could wrap our component in a Provider component, or we can export the raw component. Here's what a wrap of the Provider looks like, and I'll collapse the sidebar so we can see this, but you can see that this really mimics what's going on at our application's entry point. I reference the Provider component, put the ManageCoursePage inside, and then also reference our store. Now, of course, I haven't added the necessary imports up top. I'm just showing you how this would generally lay out. This would be one way to get things done. This approach is useful if you want to test Redux connect-related code like mapStateToProps, but I'll show you my preferred approach for that in a moment. I prefer option number two, so I'm going to undo what I just showed you there. And option two is for us to update our component to export the raw unconnected version. This will allow us to test it directly without the complexity of setting up Redux's Provider and store. To do this, first we need to open the ManageCoursePage, and all we need to do is add the export keyword right here. So now we are exporting two different things from this file. We're exporting the plain ManageCoursePage component, and our default export continues to be our connect right down here. So we're exporting our connected component by default, or we can use a named import to get our hands on the unconnected component. Now we can go back to our test, and all I have to do is add curly braces around ManageCoursePage because now what I'm doing is using a named import to reference this class rather than the connected class that is exported by default. So quite a simple change. The great thing about this approach is it doesn't break any existing import statements. Our default export will still work just fine. So if someone somewhere else imports like this, that will continue to work just fine and get the default export. And this is what we'll use for testing so that we can get the raw component. Great! So now we have our hands on a plain React component, but when we rerun our test by hitting Save, we're still getting an error, and the error is that we cannot read property 'map' of undefined in the SelectInput. So let's open up our SelectInput, and on line 15 what we can see is the SelectInput expects to receive options. And if we go down here, we can see that options is an array. So we are expecting to be able to iterate over this array, but it's receiving an undefined value because we're not passing options down right now to our component in our test. So let's go back to our test. And if this confuses you, remember that the mapStateToProps function was taking care of this. But since we're now using the raw component, we need to handle this ourselves. So one simple way I can do this is just add an authors prop right here and then pass it an empty array. And once I hit Save, now we can see that our test is passing. Now let's try to get our hands on the Save button. For this test, we want to test that when the user clicks the Save button without entering a title an error is set in state so that the user is aware that the field is required. So let's use Enzyme's find function to find the Save button. Since it's the last input on the page, I'm using Enzyme's .last selector, but as you can see, it reads quite nicely. Remember, this is searching through our Manage Course component and all of its children. We could, of course, grab it by class or ID if one were available as well. Now let's confirm that we're holding a reference to the Save button. We know that it should have a type of submit, so we can confirm that with a test. We'll just say that we expect our Save button to have a prop type that is submit. No other inputs on the page will match that filter. And if we hit Save, we can see that's working. All our tests are still passing. Again, I love how terse Enzyme and expect are. Now let's use Enzyme's simulate feature to simulate a click on our Save button. You can simulate any event that React understands, click, change, blur, etc. And now that we're simulating the click, we're greeted with a new error, and it's that it cannot read the property 'saveCourse' of undefined. Now that's because we're not passing a course in on props. Now that's easy enough to solve. Let's just copy and paste the empty course from the mapStateToProps function over here. So I'll go back to the ManageCoursePage, come down here. And remember, in mapStateToProps we have an empty course that we're defining, so I'll just copy it, and we can paste it in and use it. What I'll do is define a constant that holds my props and remove my equal sign here and the let keyword, and we'll reformat this to have a colon. And what we'll also do is put authors up here. What we're going to do is just define all of our props up here in an object and then pass them down to the ManageCoursePage right here. So instead of just specifying authors, I'm instead going to use the spread operator. There we go. Now the spread operator here gives us a nice, terse way to pass all the props that we defined up above here to our component. Remember, earlier we used the spread operator for copying arrays in our reducers, but the spread operator allows an expression to be expanded anywhere multiple arguments are allowed such as multiple arguments for function calls, multiple elements for array literals, or multiple variables for destructuring assignment. Now this moved us forward, but as you can see, we're still getting an error down here in the console. In this case, I just have a syntax error to fix. Hit Save there, and we're greeted with a new error. Yes, we're seeing a lot of errors, but we are making progress, trust me. Well we are missing a semicolon here, although that's not the root of our problem. So our new error is that we cannot read property 'saveCourse' of undefined. Remember, we were defining this function within our mapDispatchToProps function., so our function isn't getting the expected prop anymore. To fix this, we can just create a simple mock for that. And I'll just paste it in here. Remember that all of our actions were getting passed under a property called actions, so I'll define saveCourse under that and then just define it as a function that simply resolves the Promise and returns. So this is really a no-op as in it does no operations, but this should solve our error. Now we're passing more of the data that's expected for our ManageCoursePage component and its children. And again, now when I hit Save, our tests are passing again. Of course, I could've just set all these props up upfront, but I think it is helpful watching me walk through this and just seeing the reminder of what our component needs for us to be able to test it. Obviously, when we're testing, it's going to expect to receive the same sorts of props that it would receive when it's actually in production. And now that we've simulated the click, we want an error to be set. We haven't written the code to make that happen yet, but let's go ahead and add the expect. So we're expecting that there'll be an error stored in state for the title property that contains this particular text. Of course, when I hit Save now on this, this is going to fail because it's going to be comparing to undefined. We don't have any code yet that's going to set this up, so that's now our next step. Effectively, now we're at a place where we can go ahead and have a TDD experience. We have our test written that once we get this test to pass, we should feel very comfortable that we have successfully built our feature. So now that we have our test all set up, let's add the feature so that we can make our test pass. Let's trigger some validation code in our mapStateToProps saveCourse function, which is right here. And right after we prevent default, let's call a validation function that says hey, is this course form valid? Now we haven't created that function yet, so let's go ahead and do that now. And I'll just place it right here above saveCourse. So what we can see is it sets a Boolean that keeps track of whether the form is valid. We start off true, and we're going to have an object that holds all of our errors. What we'll check is if the length of the title that is posted is less than 5 characters, then we'll go ahead and set this error message, and we'll also note that the form is now invalid. The other thing that we do at the end is update setState. Since this is in state, we'll now be storing the updated list of errors within our state. Finally, we return a Boolean that determines whether the form is valid so that we know whether to move forward in our saveCourse function. And while I was yammering on, at some point I did hit Save, and what you can see is our test is passing. Success! Alright, we should be all set to check this in the browser and make sure it works. Of course, I'm just running test:watch right now, so I'll need to come down here and say npm start. And when we go to the browser and click on Courses, I'll hit Add Course. Now when I hit Save, there we go. It's working. So we have our validation showing right here. And now we've seen how to test connected components, but what I didn't address was how to test the Redux-related pieces like mapStateToProps. Let's look at that in the next clip.
When working with Redux, the mapStateToProps function can often get large and complicated, especially as we start adding complex filtering, sorting, and mapping to our application. So how do we test it? As you'll see, I prefer to extract significant logic out to a separate function, so let's see how that's done. For an example of extracting mapStateToProps, let's look at the mapStateToProps function in ManageCoursePage.js. Here's some logic for formatting author data for the drop-down. Let's extract this to a separate function. I'm just going to cut it out of here, and let's extract it out to a new folder. We'll create a new folder and call it selectors. And inside the selectors folder we'll create just one file, and we'll call that file selectors.js. Now what I'm going to do is paste in a slightly modified version of that code. All I have is one function here that I'm exporting, which is authorsFormattedForDropdown. So I would call this a selector. It is selecting data. That's why I've placed it in the selectors folder. So anytime that we have complicated data selection or manipulation code, we can consider placing it here. And, of course, we could always split this up into multiple files like author selectors and course selectors if we find that this one file gets too unwieldy. Now if you prefer, you could also just export this function from your reducers file, but I appreciate the separation here since it serves a different purpose. Also, if you choose to use Reselect to memoize functions, Reselect recommends placing your selectors in a folder called selectors as well. So if we were using Reselect, this function would only rerun when it gets new parameters. That's really useful for expensive operations. Of course, now we need to update the ManageCoursePage component to use the selector function instead, so let's go add an import. I'll just jump back to the top, paste this in, and I'm just using a named import here. And, of course, since authorsFormattedForDropdown is a function, we need to pass it the data that it expects, which is, in this case, state.authors. And now that we've done this, we can easily test the function. So let's go create a new test file on the selectors folder, and we'll call it selectors.test.js, and I'll just paste in the new version here. This should look pretty familiar by now. There's nothing new or novel going on. We're just asserting that the array is reformatted as expected. And I'm nesting describe calls here just so that this displays nicely in our results. Now since we just created a new test file, if you are running your test:watch already, then you'll need to stop it and restart it. I'm actually not running mine at this point, so I'll have to say npm run test:watch. Just remember anytime you add a new test you do need to stop your test:watch and restart it. And it looks like I've made a typo somewhere. Can't find. Ah, I typed authorSelectors here. Let's try this. There we go! And now it's passing. And there's one final detail that I almost overlooked. I ran npm run lint after running our tests and realized that we do have a linting error that ManageCoursePage is being used as an identifier for a default export. It's a bit of a mouthful, but we can see what's making it cranky right here. And it's the fact that ManageCoursePage here on line 7 is exported as a default export and as a named export. So this is something that linting checks for. So for us to tell linting that this is okay, that it doesn't need to worry about it, we can add this rule right out here. We are saying disable just this line, so we will disable checking for this particular rule just in this case. This will be necessary if we're wanting to export both ManageCoursePage as the default and as a named export. Now that I've added that, if we come down here and run lint again, then we can see now that all is clean. Great! So bottom line, if you need to test mapStateToProps, consider just extracting the complicated pieces into separate selectors, which is really just a name for plain, pure functions that are easy to test. And then you can also consider using Reselect if the function is expensive to run. So now we've seen how to test React components using ReactTestUtils and Enzyme. In the next few clips, let's shift our focus to testing Redux.
Testing Action Creators
Now Redux's action creators are so simple that unit testing them is a breeze. So let's jump back into the editor and see how easy this is. Action creators are conceptually very simple. They just return an object. So unit testing an action creator is very simple as well. All we need to do is assert that it returns the expected object. Now before we do this, I want to be honest with you. I find unit testing something this simple rather silly. We're basically going to end up repeating ourselves verbatim in our test. I find the integration style store test that we'll write at the end of this module more useful. But let's go ahead and write this, and you can decide whether it's worthwhile. To begin, we need to create a new test file for courseActions. So I'll go over here to actions, say New, File, courseActions.test.js, and I'll just paste in the result because it's really not much to take in. Nothing new going on up here in the imports. Obviously we're importing action types and actions because we do need a reference to those, and then I'm just nesting describe blocks so that this lays out nicely when we run our tests. What you can see I do in my arrange is set up the course that we're going to pass in to our expected action. So we're going to --- our expected action declares the shape of the action that we're looking for, and then we will call courseActions.createCourseSuccess and pass it the course that we declared up here. Of course, at the end, then we just assert that the action is equal to the expectedAction. Pretty simple. This is the code that it takes for us to assert that our function returns the object shape that we were expecting. And if we open up the terminal and run our test, we can see we now have nine tests, and Course Actions is one of those and is passing successfully. So that's it. Not much more to say about testing actions. Let's test something more interesting now and turn our focus to testing reducers.
Now here's the fun part, unit testing reducers. Since reducers are pure functions, they naturally lend themselves to unit testing. In fact, the creator of Redux, Dan Abramov, said, "one feedback I hear particularly often about Redux is that people who never wrote unit tests for front-end apps started writing them because it is just so easy to test reducers." You don't need to mock any dependencies or simulate AJAX requests because any new data comes in the form of actions. So you can just call the reducer with a state and an action and assert that its output matches exactly what you expect. So testing reducers is as simple as it gets. Given this input, assert this output. Since reducers have no side effects, they're easy to understand and test. In fact, writing reducer tests is so simple that you can even automate the creation of reducer tests using a library called Redux test recorder. Now, this project is currently experimental, and the API may change, so I'm not going to use it in this course, but just the fact that it exists says something about the beautiful simplicity of reducers. Alright, let's jump into the code and create our first reducer test. We're looking at our courseReducer file. Let's create some tests for it. To begin, we need to create a new file in our reducers folder, and I bet you can guess the name by now, courseReducer.test.js. And we need to add some imports to get started. First, let's import expect. We'll of course need a reference to the system under test, so we'll add in a reference to the courseReducer file, and we'll also need a reference to the related courseActions file. Our courseReducer handles the creating and updating of courses, so let's first create a test for how it handles the CREATE_COURSE_SUCCESS action. To begin, let's describe the file that we're about to test, which is the Course Reducer. Then we need to name our test, so we'll call it should add course when passed CREATE_COURSE_SUCCESS. Now we're ready to write our test. Note that I'm not going to create a full course object here since we don't need all the properties. So instead, for the initialState, I'm just going to put a title in for each of these. That's all we need to be able to run our test. Now we also need a new course to work with here, so I will say const newCourse, and this course will have a different title. We'll just say C instead. And the final thing that we need in our arrange section is a reference to our action, so we will call actions.createCourseSuccess, and we will pass it the newCourse that we just defined up above. So we have our arrange section all set up. Now we're ready to act. To act, what we need to do is declare a constant called newState, and now we can call our courseReducer. And anytime we call our courseReducer, we need to pass it initialState, which in this case is going to be the initial state that I just declared up above, and the action, which we also just declared up above in our arrange. So this completes our action. Now we're ready to begin our assertions. The first thing that we should be able to assert is that there are now three items in state. So let's begin by saying expect(newState.length).toEqual(3) because we are expecting there to be three items in state. Now at this point, it might be nice to just say npm run test:watch so we can see if we're in good shape so far. There we go. Excellent! And our reducer test is right here, and it is passing. We're going to put a few more expect statements in here because there's some other things that we should assert in this test. Another thing we can do is say that the first element in the array should have a certain title. We expect that title to equal A. We'll hit Save, and that passes as well. And in fact, I could copy this, paste it down. I could check the second element and third element have what we expect as well. Hit Save, and there we go. We've now written our first test for a reducer. Pretty straightforward. We can do something pretty similar to test update. I'm just going to paste in the final update, and then we can talk about the differences. So let's add that here. So obviously our name is different. You can also see that I've had to add a little bit of extra data here because we do need an ID so that we can get our hands on these records and test them properly. Of course, when we're updating a course, our reducer needs that data to be able to update it. I like this principle of just specifying the data that we need here though. It helps keep our test a little bit more readable. And you can see that down below we're just asserting that the updated course's title is equal to the New Title that we specified and that the untouched course didn't have its title changed. Also, we're making sure that the length of our array of newState is what's expected. We expect it to still be 3, so that proves that we didn't accidentally insert a record when we thought we were updating. And when I save that, we can see that all those tests pass as well. And that's it. As you can see, testing reducers is quite straightforward. Now let's take a look at testing thunks.
Thunks handle asynchrony, often dispatch multiple actions, and also often interact with web APIs. This makes them a little trickier to test. So to test a thunk, it's going to require some mocking. We need to mock two things, the store and any HTTP calls that we make. We'll mock the store using the redux-mock-store library, and you can mock any HTTP calls using Nock, which stands for Node mock. Now in our app, we're already using a mock API that doesn't generate actual HTTP calls, so we don't need Nock, but I'll show you how to set Nock up regardless. Okay, back to the editor. Let's write our first thunk test. Okay, we're looking at our courseActions file, and we can see a couple of thunks here. To test our first thunk, let's go create our test file, and we'll call this courseActions.test.js. Oh yeah, we don't need to create it. It's already here. This should not surprise me. We created this earlier. Okay, well one step down. So to test our first thunk, we need to add some imports up here to the top of our file. And we'll add these in. We're going to need redux-thunk, Nock, which I mentioned earlier is for mocking HTTP calls, and finally, a call to redux-mock-store, which lets us configure a mock store. And we'll put our test for thunks down here below. To begin, I'm going to configure our mockStore. Our mockStore takes an array of middleware. The only middleware that we need to work with here is Redux thunk. So I'm just declaring an array with just Redux thunk inside, and that will give us a mock store right here on line 30. And with that prep work done, we're ready to begin writing our test. We're going to describe this as Async Actions, but I'm also going to define here an afterEach function because it's important with Nock that after each call we call cleanAll, and this just performs a cleanup after each one of our tests is run. And now we're ready to write the body of our test. The name of our test is going to be a bit of a mouthful. In fact, I'm going to collapse the sidebar so we can see it all here. We'll call it it('should create BEGIN_AJAX_CALL and LOAD_COURSES_SUCCESS when loading courses'. And I will close out my brackets here. At this point, as part of our test setup, we would call Nock. Now I'm going to paste this in, but I'm going to keep it commented out because we don't need it since we're just using a mock API here. But if we were hitting an actual API, then I would define the precise URL that we're planning to hit with this test. And what Nock would do is capture that call and then instead return this fake response instead. So I can hard code in the response that I'd like to receive instead of making an actual HTTP call to the address that I specify. But again, since we're hitting a mock API, I'm just going to skip that step. It's not necessary for us. Next, let's declare the array of actions that we're expecting as part of this thunk. There are two actions that we expect. We're expecting BEGIN_AJAX_CALL and LOAD_COURSES_SUCCESS. And within the LOAD_COURSES_SUCCESS, we're expecting this payload to be part of it, a body with courses and then this within the list of courses, just one course with this inside. So this is effectively our test data that we're setting up here in the arrange section of our test. For the next step, we'll put our mockStore to use. So on this line, what I'm doing is calling the mockStore and sending it some initial state and the actions that we're expecting. So this line puts our mockStore to use. Now we're ready to dispatch from our store and see what happens. What we can see here is that we're dispatching the loadCourses call and then calling store.getActions to get our list of actions. This finally allows us to make our assertions. We will assert that the first action is BEGIN_AJAX_CALL and the second action is LOAD_COURSES_SUCCESS. And then finally, we use this callback right here to declare that we're done, and that completes the asynchronous flow. And with that, we should be able to open up the terminal now and run our tests. And by the way, I could just type npm test there because that is a shortcut. And great! We can see that it's passing. Now there is one problem though. What you can see is it took 1009 ms. If we have slow tests, they get labelled in the list. And this really shouldn't surprise us because remember I'm not mocking out the call to our mock API, so it is actually going to the mock API. And because our mock API is configured right now to delay all our calls by 1 second, our tests are slow, and they take 1 second. So one thing that we can do is just open up the delay.js file and change this 1000 to a 0. And when I hit Save, now our tests pass very, very quickly. We no longer see --- well, I take that back. It does look like it is still --- oh, never mind. It's because I need to rerun the tests. I didn't run them in watch mode. That makes all the difference. Let me run them again. And great! Now we can see that it only takes 284 ms. If we go back up here to our async actions, it's no longer being listed as a slow test. So we're in good shape. Now on a real app, you'd likely want to set the delay to 0 when running your tests. And for simplicity, we're just changing the hard-coded value here. You might consider having different values for when you run tests than when you're doing development because I do find having a delay during development is helpful just so we can see our AJAX calls going back and forth in a more realistic manner. And this is the nice thing about having a mock API. We can run our tests that utilize the mock data, and they'll run extremely fast and reliably because the data is static and will be held in memory. It sure beats creating and maintaining a real test API in a database behind the scenes for integration testing. But to clarify, you don't need to use a mock API to be able to test your thunks. By using Nock, you can trap any HTTP calls and instead specify a fake response that will be used by your test.
Testing the Store
Let's wrap up this module by writing an automated test of the Redux store. When testing the store, we're really writing an integration test rather than a unit test because our goal is to assure that our actions, the store, and our reducers are interacting together as expected. So we're going to write some tests for the interaction of these three pieces. This is the last test that we're going to write, but frankly, I really find this style quite useful. And I'm not alone. James Kyle posted a poll on Twitter about this very topic. Thirty-four percent of respondents test action creators and reducers independently like we saw in the previous clips, but 29% of them test these items together at least some of the time. And for what it's worth, Dan Abramov, the creator of Redux, suggests testing reducers, selectors, and action creators together as well. Yes, this isn't a unit test, but it gives us a lot of value without requiring much work. So I'm calling this a store test for brevity, but understand our goal here is to test the interactions between Redux's key pieces. Alright, let's write an automated integration test for our Redux store. To test our store, we're going to create an integration test rather than a unit test, so let's start over here and create a new file and call it store.test.js. To begin, let's add the necessary imports. As you can see, we're going to reference expect. We'll need the createStore function because we're going to need to pass that to our rootReducer. So, of course, we'll also need our rootReducer. We're going to reference initialState. And finally, we'll need a reference to our courseActions in our test as well. With our imports all set up, we're ready to add our describe statement. And at this point, we're going to describe our store. And here I'm just using the function keyword instead of the arrow function. You can do either, whichever you find easier to read. Now let's name our test. In this case, we'd like our test to be it('Should handle creating courses'. So we're going to test that our action creators, our store, and our reducers work together as expected to create a course. We need to begin with our arrange. So let's start by creating our store. We'll create a constant called store, call createStore, and then we will pass this our rootReducer and our initialState. So this looks exactly the same as the call that we make to createStore in the application's entry point. And now let's set a constant that holds a course which we want to create. I'm not going to create the full course because we really don't need that to be able to test our interaction here. I'll just create a single course, and we'll just give it a title. We'll call this course Clean Code. Okay, and now we're ready to act. That's all the arrange that we need. So to act here, we're going to call an action creator. I'm going to use a constant to hold a reference to the results of this, but we'll call courseActions.createCourseSuccess and then pass it the course that we just defined up above. Now that we've done this, we have a reference to our action creator, so we can call the dispatch function on our store and pass it the action that we just set up. Now we're ready to assert. In this case, we're going to use the getState method on our store to be able to get the state out of it so that we can write our assertion. So what we'll do is create a constant called actual here because what we're going to store is the actual results of what is now stored in our store. And I will call getState().courses, and we'll just get the very first course. So now we are calling getState, which gives us all the state that's in our store, and we're saying we're looking for just the first course because we're expecting that the store started out with initialState, which is an empty array of stores, we're adding one course to the store by calling createCourseSuccess and passing it the course that we defined up here, and now down here we're expecting to find that one course in our store. So at this point, we're ready to write our expected value. Of course, in this case, what's expected is that we get back an object that contains what we declared up above. So we're expecting to get back from this call to getState().courses 0, a single course, which is the Clean Code course that I defined right up above. So our expect call is now quite simple. I will expect(actual).toEqual(expected). And with this, we should be able to open the command line, say npm test. And again, you can say npm test or npm run test. And excellent! We have that passing. Let's see where it sits here in the list. There it is, our store. Our store should handle creating courses. So as you can see, it didn't take much code to write a nice integration test that assures that all these pieces are working together as expected. Now we could even go further with this and create an array of actions up here and then assert that the final result was what we expected. So I could dispatch two createCourseSuccess actions and an updateCourseSuccess action and then assert that the final store has two courses with the expected values, but I'll leave that as an exercise for you. And that's it! You've now seen how to test all the major pieces of React and Redux. Let's wrap up with a short summary.
In this module, we wrote tests for each of our application's major file types, connected React components and Redux's action creators, thunks, and reducers, and we wrapped up by creating an integration test of our Redux store. So we're almost done. In this final capstone module, let's set up a production build process that prepares our application for actual use in the real world. And I'll close out the next module with a list of challenges so that you can put these new skills to use to enhance our app.
Setup Production Redux Store
Our first step is to configure our Redux store for production. Our current store that you see here contains code that we don't want to run in production. See, reduxImmutableStateInvariant is really only useful for development. It would cause us performance problems and is really just there as a development warning that should occur when we accidentally mutate state. So in order for us to create a store for production, we need to create a separate configureStore call for both development and production. And here's how I'll do this. I'm going to rename configureStore to configureStore.dev.js. And then I'm going to copy this file, and we'll paste a new version of it, and we'll call it configureStore.prod.js. Now that we're creating the prod version, the only difference in the prod version is we don't want reduxImmutableStateInvariant because that's really only useful for development purposes, and it would just slow our app down unnecessarily. And that's it. So now we have a dev version and a production version that are slightly different. But the question is how do we put each of these to use? For that, let's create configureStore.js. And inside here, we'll just call the appropriate configureStore call based on the Node environment that's currently set. So great, now when we deploy to production, our application will use the lean simple store that we just created that contains only production-specific concerns. There's one final tweak we'll need to make now that we've made this change. We're no longer defaulting export from this file, and our linting rules expect us to export default from each file. So we'll need to come over here to the place where we're consuming our store, which is, of course, at our application's entry point, and then at the top of the file we just need to put in an eslint-disable for our import/default rule. This way, ESLint won't complain about the fact that we're using a file that doesn't have a default export. Now you're probably wondering where the Node environment gets set up. We'll take care of that in the next clip as we configure webpack for production.
Setup HTML Build
Setup Dist Server
Now this isn't required, but I like to run the final production version of the app on my local machine just to make sure that everything looks good. This can be helpful when you need to debug an issue with the production build. So to do this, let's go over to the tools folder and create a new file. We already have a srcServer. Let's create a distServer.js. So srcServer serves up the files in our src directory. DistServer will server up the files in our dist directory. I'll just paste this in because it's very similar to our srcServer. But here's a quick div so you can see the few lines that I changed from the development version. As you can see, I removed all the webpack and hot reloading-related code since we're just serving plain static files via Express. And on line 11 I configured Express to serve static files. That's it, nice and easy. And we're almost done setting up our production build. The last missing piece is adding some scripts to our package.json file, so let's do that next.
Setup npm Scripts
To automate our production build process, we'll need to add some npm scripts here to tie all this goodness together. It's only going to take six lines to orchestrate all of this, so I'll paste this in, and then we can talk through it. As you can see, we have a prebuild, build, and postbuild step here at the bottom. By convention, prebuild will run before build, and postbuild will run after build. The prefix tells Node what order to run these scripts in. As we can see, in the prebuild step I'm using npm-run-all, which is a cross-platform way to run multiple commands either one at a time or in parallel. Here I'm not passing the parallel flag, so the clean-dist script will run, then test, then lint, then build.html. As you can see, in the clean-dist script, what I'm calling is yet another script, remove-dist, which sits right below it. What I'm doing here is getting rid of the dist folder and then recreating it. This is a good idea just to make sure that we have a completely clean build so that any old files are removed. I'm using rimraf, which is a cross-platform way to be able to remove a folder forcefully. This is really the equivalent to running rm-rf if you're familiar with UNIX systems. But since this is a Node module, it will run on Mac, Linux, and Windows just fine. And as you can see in the clean-dist folder right after I removed the directory, I create it. Thankfully, mkdir is a cross-platform friendly command, so that will run on all platforms as well. Now that we've discussed clean-dist, you can see back on prebuild I'm also calling test and lint so that those are run before our build. We want to make sure that all our tests pass and that our code lints successfully. And then we're calling the build:html step. As you can see, I'm just using babel-node to run buildHtml, the file that we just set up to transform our HTML file for production. Of course, I'm using babel-node because we wrote that in ES6. Babel-node will transpile that down to ES5 so that Node will be able to parse it even if you're not running the latest version of Node. When we look at the actual build step, we can see that we're calling build.js. And remember, that file will just run our webpack build and generate our final bundle.js, as well as extracting our CSS into a separate file. And finally, postbuild will run after build, which will open up our distServer so that we can see the results of our work. And now that we have our scripts set up, we're all set to give this a shot. Let's try running our production build script and see if anything bursts into flames.
I believe that until you've really tried this on your own you don't fully know it, so I'd like to wrap up this course with a few challenges for you. I've deliberately left quite a few interesting bits out of this demo. So if you're looking for some ideas to try on your own, here's some challenges to consider. Challenge 1, author administration. Try adding support for administering authors as well. And here's a hint. Be sure to add some logic that makes sure you can't delete an author who already has a course. Delete course. Add functionality to delete a course. And once you've added the power to delete a course, make sure that you hide the empty course list once all courses are deleted. Message to the user if they try to leave the ManageCourseForm without unsaved changes. And enhance the ManageCourseForm client-side validation to validate things like the category and the link data as well. This is a surprisingly fun one. The special challenge of showing 404s is on the ManageCoursePage. Here's my hint. You're going to need to add some logic to mapStateToProps. Or you could try showing the number of courses in the header. This is a great example of how Redux's single-store model really pays off. You'll see that adding this is really trivial, and there's no worry of it getting out of sync. You could add pagination or infinite scrolling to the tables that we're using in order to support large data sets. And it'd be nice to sort the course table alphabetically by title by default so that the last record updated or created isn't always placed at the bottom like it is right now. And I'll give you a hint here too. MapStateToProps is where you want to get this done. Finally, consider keeping the old values for course data so that you can revert changes when the user navigates to a different page without saving. These are all features that I had hoped to cover, but I had to cut them from the course to keep the length down. I walked through how to implement quite a few of these in my React and Flux course, so if you're curious or find that you get stuck, you might want to take a look over there. And that's a wrap! As we saw, a production build process requires some extra work, but it pays off huge for the end user. Our dev environment was over 4.8 MB, but now it's only 121 K in production. That's a huge time and bandwidth savings, and it all happens via a single, repeatable command.
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.
Released20 May 2016