Intro
NOTE: This is an older version of this workshop. Go to The React Learning Path to find the latest version of Complete Intro to React
Welcome to A Complete Intro to React. The goal of this workshop is to get you full up to speed on modern development and give you an idea what it is like to develop an app in the React ecosystem.
When talking about React, you cannot simply just React. It is an incomplete picture; it is a cog in the machine. React does well to introduce some useful primitives into your toolbox and allows you to build your app without introducing too many opinions. As such, we need to learn some other tools to round the whole story of our app. However, keep in mind that these are modules we are choosing to plug together; you are free to go home and swap in your own parts to suit your own needs. While React fits many/most needs, this complete stack won’t; it has opinions and you need to make sure each piece contributes to your story and try not to shoehorn any piece in that you don’t need.
In addition to React, we are going to be using node.js, express, redux, WebPack, Mocha, Enzyme, npm, and react-router. Don’t worry if you’re aren’t familiar with any of these: that’s the point of this workshop. Since this workshop is about React, we will not be deep diving into node.js or database schema. We will discuss these satellite concepts just as they pertain to React.
Questions? Feel free to tweet at me. Corrections or concerns about this page? Please file an issue or make a pull request on the repo.
Setup
In order to be able to complete this entire workshop, we need to go ahead and get some tools downloaded. Even better if you can complete this before the workshop since conference Wi-Fi is notoriously spotty.
Clone the git repo
node.js 4.3+
You probably can complete this with something less than node 4.3 but that’s what I’m using. Being the node.js LTS release, it’s a safe bet to be using. If you need to use a different version of node for work, I strongly recommend using nvm. I recommend getting the binary straight off the node website and not using homebrew for installation.
npm installs
Run npm install
from the directory where you downloaded the repo. If you have node and npm installed, you should see a list of dependencies being installed.
npm global installs
Run the following global npm installs
We’ll be using webpack v1.12, Mocha v2.4 and standard v6.0.
React
Welcome to the wonderful world of React. We are going to start with the most absolutely barebone version of React. No JSX. No ES6. No transpilation. Just pure component-oriented pleasure of coding.
So let’s talk about what React is. I imagine most of those who take this class will come from another framework or library, be it jQuery, Angular, Backbone, Ember, Knockout, or something else. I will draw comparisons and contrasts to these other frameworks (despite not being the same, I’ll be using the word library and framework interchangeably for brevity’s sake) to help illuminate some of the differences but you needn’t have programmed in these other frameworks to understand this workshop.
React is a library to build the views in your app. In truth, it shares more similar responsibilities to jQuery than it does to Angular or Ember: it is not a the framework but rather a component you’ll use in constructing your own framework. If you’ve done Backbone, React is just the view but not the model or component. While it can do a little data management (which is sufficient for small apps,) you really need some other construct to manage your data if you are doing some sort of app. We will get to that later using redux.
It bears mentioning that we are slowly going to build a full stack app progressively over the course of this workshop, piece-by-piece. By the end of the workshop, you’ll have built a real-time app that does streaming video.
My First Component!
So let’s start our first React code!
My Second Component
Good job! You should see the text “This is my first component!” on the screen. As you may see, we constructed a bit of DOM using functions. That’s all components are in React: functions. This ends up being a useful and powerful abstraction which you hopefully will see as we go on. This is about as simple as it gets as far as a React component goes. Let’s take it one step further by nesting some components.
Before we start here, let’s delete that bottom script tag in index.html and add <​script src="js/ClientApp.js"></script>
so we can get some good code separation going. Once done with that, create the js directory and add the ClientApp.js file. In this file, let’s put
Cool, right!
MyTitle
is a ReactComponent
class. This is a constructor function that, once invoked, is expected to return a JavaScript object with at least a render
method on it.
To invoke this constructor and create a new instance of the MyTitle
component, you have to use the React.createElement
method. The resulting instance is a ReactElement
.
We can use this element the same way we use any other HTML-native tag. This allows us to encapsulate style, behavior, and markup into one neat little package and reuse these components everywhere!
To sum-up, we’re using createClass
to create the component and then we’re using createElement
to create an instance of that class, resulting in an element that can be used in others components.
Factories
This is a bit verbose to write React.createElement
so many damn times. Let’s use a shortcut.
We can use createFactory to side step this. Now we can use our class like we use div. In fact React.createElement('div', null)
works the same as React.DOM.div
. The one I showed you is just a convenience factory method that React provides for all of the HTML tags.
Props
Our title component is cute but not super reuseable. Let’s make it a bit more flexible by using some props. Props are variables that you pass from the parent to the child but the child cannot modify the props it gets. This simple restriction helps a lot in the future because when bugs arise, you know the child didn’t modify the variable because it can’t! Let’s see how to do it.
Now we can change the contents of the title. But since we can pass in lots of props, we can widely differing elements of the same class based on what props are passed into the element. Let’s take it a step further (and show you how to do inline styles and attributes with React.)
Let’s stop there and switch our attention a bit to tooling. So far we’ve been writing React with no compile step which is pretty cool and not something enough do in the course of React. Certain things will just make sense because you know what it complies to. In any case, onward!
Tooling – standard and npm
Before we can introduce JSX to React, we are going to have to send it through a compilation step. So we are going to take a brief repose from React here to start working on our tooling a bit.
standard
Let’s start with the easiest to start with right now: standard.
standard is a linting tool. Specifically, the executable version of standard that you get when you npm install -g standard
is a wrapper around a pre-configured eslint. If you are unfamiliar with linting tools (like jshint, jslint, jscs) the basic gist is that this tool programatically checks for certain violations of code style in your code. A common (and arguably the most useful) check that it does is make sure you have no unused variables and that you make use of no undeclared variables in your code. A common reason to trip either of those rules is that misspelled a variable somewhere. There are dozens of other checks standard does: check them out here.
A note on standard, since it inevitably causes controversy. standard is a set of eslint rules that is meant to not to be configurable to totally sidestep the conversation of what rules include and what not to. It’s meant to be a step in the direction of a standard format in the JavaScript community, much akin to how the Go community has standardized on one. Perhaps the most controversial rule in standard is no semicolons. It’s okay to not use semicolons in JS. Really. I promise. It’s not slower and it won’t introduce crazy bugs. It’s okay to use them too! It doesn’t particularly matter. However, in this workshop, you code won’t pass lint if you use semicolons. If you insist that you need your semicolons, you can use the package semistandard. But give standard a shot. You may like it.
That being said, we will introducing new rules for our JSX since we want some rules added to enforce good “Reactful” code.
So, head to your terminal and navigate to the directory of the project. Given that standard is preconfigured to recursively check all js and jsx files recursively in a project and leave out some sane defaults (it doesn’t check node_modules/) you should be able to just run standard
from your terminal and see if you have any issues. If you do, go fix them! If you feel so inclined, you can use standard-format to have your code auto-fixed for you. I’ve found it sometimes has some derpy indentation but you may have better results.
npm scripts
One of the nice convenience that npm allows you is its scripts features. If you work on a team or an open source project, it’s a great way to standardize how things are done in your project. Even if it’s just you, it’s an easy way to shorten commands. So go to your package.json and add:
Once in your package.json, now you can go to your terminal and run npm run test
and it will run standard for you. This isn’t a big right now but we’ll start chaining our whole testing process together and we won’t have to remember the complex stuff we’re doing each time. Furthermore, npm run test
is a standard in the node community and should work in just about any project. We’ll have several scripts in here and it quickly becomes very convenient.
Since all our tests, lints, and builds will be done through npm scripts (which are just commands piped to the terminal) we don’t need any tools like Grunt, Gulp, or the like. Anyone who has ever maintained a Gruntfile knows why this is such a big deal.
Tooling - webpack and Babel
Now that we have clean code via a linter and can run our scripts courtesy of npm, let’s work on the compilation / build of our code. In order to achieve this we are going to use two tools: webpack and Babel.
webpack
webpack is an amazing tool that came out two or so years ago that captivated particularly the React crowd quick due to its ease and interesting features. We are going to only scratch the service of this powerful tool; you could give a whole workshop just on webpack. We are going to lean on two of webpack’s core features: module compilation and the ability to plug in loaders.
As you saw with our code, it is pretty to put one or two components in one file. But what happens when you have an app as complex at Netflix that has hundreds, if not thousands, of components? Having just one file is untenable. You could just split them into separate files, make sure to load them in correct order, and keep a list of files in your HTML directory. But that sucks too, so we’re going to introduce a build step that, while build steps suck, this will make things a bit easier.
For fun, split your MyTitle component from ClientApp.js into a new file, MyTitle.js. You will have to put the appropriate React.DOM helper methods in each file. At the bottom of MyTitle.js, add the line module.exports = MyTitle
. At the top of ClientApp.js, put the line var MyTitle = require('./MyTitle')
. Let’s try to compile that.
You should have webpack 1.12+ installed (this is untested on 2.0+.) Go to the directory of the project in the terminal and run mkdir public
. After that, run webpack js/ClientApp.js public/bundle.js
. In your index.html, change the line of <​script src="js/ClientApp.js"></script>
to <​script src="public/bundle.js"></script>
.
Try your browser again. bundle.js has all the stuff for both files now. Now we can keep components in their own files which is a huge win for organization. But wait, we can use webpack for even greater code by using it to bring in node modules. In index.html, remove the other script tags so just bundle.js is left.
In ClientApp.js, remove the global comment at the top and add at the top
Likewise at the top of MyTitle, add the line var React = require('react')
. Run your webpack command again. Try your browser again. Despite only including bundle.js, the whole app works! If you look at webpack, you’ll see it’s 99% React code and some of yours. Now we can go forth making new files and including new libraries without worrying about if they are being included!
Let’s add build to our npm scripts. Add to scripts in package.json "build": "webpack js/ClientApp.js public/bundle.js",
. Run npm run build
from your terminal in the project root directory and see if it works. Magic!
Babel
Babel is an amazing, amazing tool that has fundamentally altered the landscape of JavaScript as we know. Though they didn’t invent the idea of transpiling future syntax of JavaScript into current syntax (thanks Traceur,) they did make it so damn easy. Couple that with the fact it worked with React’s JSX (which we’ll talk about in a sec) out of the box and it was a perfect storm the be massively successful. Babel, in short, is amazing and should be something you consider for every project.
Babel 6 (the latest revision of Babel) took away a tiny bit of that ease of use but in return it became much easier to develop which is important when you’re important when you’re maintaining a massive project that’s very important. In return it requires a bit of config now.
Create a new file called .babelrc. Inside .babelrc, put:
Babel has the concept of plugins. Each transformation comes in the form a plugin. However ES6 and React each have a number of transformation and it’s a huge pain to include each one. Thus Babel has the concept of presets, or in other words bundles of plugins. In this case, we’re including all of the React and ES6 transformations. If this were a production app we would definitely go through and only include the transformations we needed and not all of them. For example, this includes a fairly large library to get ES6 generators working. Few people these days are actually using generators and thus it’s better to leave them off if you don’t need them. In our case, this is a teaching app so page weight isn’t a big deal to us. This will also allow us to start using JSX.
webpack loaders
So, we could use the Babel CLI to compile our code but we are already using webpack and webpack has a good way to send your code through Babel via its loader mechanism. Loaders are utilities that webpack will pipe input into and accept output out of. You can use loaders to transpile things like CoffeeScript, TypeScript, or PureScript. Webpack loaders can do some other powerful things like allowing you to include CSS, inline images via encoding, and transform SVGs. We’re just going to be using the JS transformation abilities for now. Run the command webpack --module-bind 'js=babel' js/ClientApp.js public/bundle.js
. Should take a bit longer since it’s doing more. Since we’re going to be using webpack for a few other things, let’s abstract that configuration from inline to a config file. Add a webpack.config.js
with the following.
What you see is essentially a config version of what we were doing with CLI flags. Now it’s a bit more resilient and guaranteed consistent. If you run just the command webpack
from your project directory you should get the same output. Neat. Go change you npm script to just be “webpack” now. Go try it and make sure it still works. Great! Also make sure all the files you’ve written are still up to snuff with standard via npm run test
. Good? Good. This should get us to a point now where we can talk about JSX.
JSX
We have been writing our React with vanilla JavaScript up until now which frankly few people do. Most people take advantage of JSX which essentially adds HTML/XML-like syntax as a “primitive” type to JavaScript. In reality, what it does it takes the HTML you write for your components and translate them into the same calls we writing just using vanilla JavaScript.
So why JSX? People usually get pretty grossed out by the HTML in the JavaScript and say it looks like 1998 when we were still writing JavaScript in our HTML. However, I assert that markup in JS is a good thing while JS in markup is a bad thing! Here, we’re keeping all the concerns of a component in one place: the markup structure, the event listeners, the state, the state mutators, everything. If the component breaks, we know it broke there. That’s really valuable.
So try it. While the plain JS way isn’t too bad, once you start writing JSX I almost guarantee you won’t want to go back. Let’s convert what we have to JSX.
Side note: good idea to install a JSX syntax highlighter. If you’re using Sublime, I highly recommend the “Babel” package off of Package Control. If you’re using VIM, good luck. I’ve heard people struggle to get it right with VIM.
Open MyTitle.js and rename it to MyTitle.jsx. It doesn’t actually matter since both are getting run through Babel but it does signify to all who follow that this file must be compiled before being shipped out.
We’re using JSX finally! Make sure you’re inserting those opening and closing parens around the component. It’s just letting JS know you’re going to put your expression on the next line (which we want to do for readability.)
Notice the curly braces surrounding this.props.title
. They’re there to let JSX know I want his to be a JS expression. If they weren’t there, it would put the string ‘this.props.title’. Notice the double curly braces surrounding the style value. The exterior set of curly braces are the same as the one as before: they’re letting JSX know you want a JS expression. The interior set of curly braces represent a JavaScript object, meaning you’re passing in a object to the style attribute. A little strange to get used to seeing that.
Lastly, you may notice that I switched to an ES6 style here. This is synonymous with the function syntax; just a bit more terse. Feel free to write it in any syntax that fits your fancy; this is very readable to me but may not be to you. Basically, since it’s an arrow function and lacks curly braces, the resulting component is implicitly returned automatically.
Let’s rewrite ClientApp.js. Rename it to ClientApp.jsx.
Also make sure you update your webpack.config.js to point the JSX path of ClientApp.jsx on the entry line.
Okay, so notice we changed to a function on MyFirstComponent and that’s why we changed the ReactDOM.render MyFirstComponent to a function call (which is all making a tag in JSX is being changed to.) Also, notice how we use our own components as if they were normal HTML tags. Neat, right? We define our own components and then we can throw anywhere since they’re self contained! We even pass them props as if they were normal attributes.
Notice we’re including React in both files but not directly manipulating it. This is okay since remember JSX is converting these tags to calls to React, so in reality it is using React (and thus we do have to include it.)
Cool, right! Also notice we dropped the factory business. No longer useful since JSX is totally fine to convert our JSX to the long-form function call. Overall, a simplification I think!
Something to make note of: the top level component has to be singular, or in other words, your top level component cannot be sibling to something else. This makes sense if you think about what JSX is transpiling to: function calls.
Hence why you’ll see a lot of wrapping divs in JSX; it’s so the whole thing will compile. This is fine if you need to do this; an extra wrapping div does nothing of harm unless you have structurally sensitive CSS. Also notice that if you have just raw text to put into JSX, you can enter it just as if was normal HTML. It’s only when you have JS expressions when you need to use the curly braces.
More React
So let’s actually discuss what we’re going to build today: a basic Netflix experience. This is going to afford us several fun experiences and use cases to explore with our new-found stack. Our app is going to have a home page, a browse/search page, and a video page. Over the next few chapters we are going to be talking about redux and react-router and their rather-central roles to the React eco-system. But first let’s keep diving into React.
Rather our toys we’ve made so far to demonstrate how to make React components, let’s actually start building our web app. We are going to make our home page. The home page is going to have a sweet background image, our logo, a text input to search for a specific show, and another button to just browse all videos.
I have pre-done all the CSS for you (since this is not a workshop on CSS) so if you just follow my CSS naming you will get all the CSS for free. It does bear mentioning here that libraries like Radium exist that allow you to do all of your styles in JS and let React and Radium handle all the styles for you. This is a pretty big departure from normal web development and not something most developers do so we’re not going to cover it. But I will say, should you feel so inclined, go out and give it a shot. I did for a little side project and had a blast with it; it’s a fun way to approach CSS since you have all the tools of JS at your disposal and don’t really have to care how it compiles down to CSS like you do with PostCSS/Sass/less. Anyway, follow my CSS and you’ll be golden. If at any time your styles look broken as compared to mine, chances are you misnamed something.
Let’s start building our app. You can delete MyTitle.jsx if you desire; you can also leave it. We’re not going to be using it any further. Go ahead and clear out most ClientApp.jsx and let’s start putting our app in here.
Oh, and you also have to name your video service. I named mine svideo but name your app whatever you want!
Right now it’s going to be pretty simple. Drop in this:
Save and run npm run build
. If you followed the CSS naming and HTML structure, you should see a nice looking landing page. Also a good time to run npm run test
to see if your code is still lint-compliant.
So, another tooling detour here: I’m getting pretty sick of having to hit the terminal every single time to see run build. Furthermore, build for webpack is pretty slow despite how small our code is. So let’s take advantage of webpack’s watch feature. This will watch for every time you save rebuild automatically. Additionally it will keep the unchanged bits (like the React library) in memory so it doesn’t have to rebuild it every time. Try running webpack --watch
in your terminal. It will use the same config we already made. See how much faster it is after running? Let’s add a new npm script.
Great, right? So, another part that’s been bothering me is that it’s such a pain to have to re-run standard every time. Either that or you’ll get a bunch of errors all at once when you run it before you commit. Luckily we can have webpack run standard each time it compiles. It will then notify you when you have errors.
Just like we’re using the babel-loader to transpile our code, we’re going to use the eslint-loader to run standard for us. eslint is what standard uses under the hood, or in other words standard is just a specific configuration of eslint. We’re going to lean on this fact and use a config file with the eslint-loader.
Create a file called .eslintrc.json
. That first period is significant. Inside the new file put:
This will configure eslint to check for everything standard has been checking. In addition, since we’re already adding a config file, we’re adding a few checks specific to React and JSX. Again, these will spare you bugs in the future and they’re super helpful. Let’s change our webpack config to use our new eslint-loader. eslint will automatically use our .eslintrc.json, regardless is called via the CLI or programmatically via webpack.
Nice! Any time you save now using npm run watch
it will both compile your code and lint it. Pretty slick. And it all runs so much faster. We’re going to get to how to make your code reloads even faster. But first let’s keep going with our app.
Our landing is pretty much done for now. We want to start working on the browse all page, but we need to move onto the router to do that real quick.
React Router
So now we have a basic landing page and we want to be able to transition to another page. Because we making this as a single page app, we are going to use the marvelous react-router. react-router is a robust piece of technology and we are just going to be scratching the surface of it now. If you intend on making a single page app, I suggest you deep dive into it to uncover all of its potential.
We are just use the top level router at the moment. First, let’s move our landing page into its own component so we can use ClientApp as the router. Move all the code (except the ReactDOM last line; leave that there) to Landing.jsx.
Cool. Make sure standard isn’t yelling at you and that your app still works. It should appear pretty much the same to you. Now we have a router so we’re free to introduce a second page! If the const { Router, Route, hashHistory } = ReactRouter
code looks foreign to you, check out 2ality’s post on it.
So now we got react-router rolling, let’s move onto our second page, the search page.
Let’s make our search page. We’re going to start with some dummy data and work our way from there. Again, follow the same HTML structure and CSS naming as me and you’ll get all the styling for free. Feel free to take a glance at public/data.json to see what’s there. As you may have guessed, it’s a bunch of Netflix shows. This whole workshop is actually just an elaborate advertisement for Netflix (just kidding; I promise.)
webpack allows you to require in json files just like you would another JavaScript file so we’ll take advantage of that when we start coding our new search page. However we do have add another loader to do that. Add the following object to your loaders
array in your webpack.config.js.
Create a new file called Search.jsx. In Search.jsx put:
Put in ClientApp
In Landing, change the or Browse All
to
Now you have another working route (which all it’s doing is showing an h1) and a link to get there. When linking between routes with react-router, use the Link component. This allows you to refactor how routes work without having refactor all of your individual links (you could just make your a’s href “#/search” and it would work for now but could break later.) Now your button should work to take you to the browser page and you should be able to use back and forward for free thanks to react-router.
React: Props
Let’s start making our search page. Replace JSX HTML in Search with
You should see it say “House of Cards” at the top of the page. When you use curly braces in JSX, you’re telling JSX you want it run a JavaScript expression and then display whatever it returns. If you take away those curly braces (try it) you’ll see it literally displays “data.shows[0].title” as a string. So that’s a neat tool to have; let’s take it a step further and display all of the titles as their own components.
As you may remember, JSX is transpiling your JSX-HTML to function calls. As such, you may be able to imagine that a bunch of sibling components are just an array of components. Since they’re just normal ol’ JavaScript arrays, we can use some functional-programming-fu to transform data into components.
You should now see all of the titles in a nice scrollable list. This is the ng-repeat/#each of React: plain ol’ JavaScript map. This is one of the reasons I love React: for the most part best practices of React are just JavaScript best practices. There’s very little DSL to learn. Cool! Let’s flesh out how our search results are going to look.
Try saving and re-rendering. You should see some nice cards for the shows. Notice that we can use those fancy curly braces to insert JavaScript expressions into HTML attribute too. Neat.
However we can reorganize this a bit better: the Show component can be broken out into its own component. Let’s do that. Make a file called ShowCard.jsx and put this in there:
Notice we’re using this strange props object being passed in as a parameter to the ShowCard component. This is what we are going to be receiving from our parents. In this case, an individual ShowCard needs to receive all the necessary data from its parent to be able to display it.
This is a good time to discuss the philosophy that’s sort of tough to get used to with React coding. We typically think of user interfaces as entities that change over a span of actions and events. Think of a jQuery UI you have made. Imagine making a drop down. You would have to write the code for a user clicking it to opening the drop down to the user clicking an item in the drop down. It’s a progression of time, events, and interactions. Imagine if there was a bug with that final interaction. You now have to work out in your head the sequence of events to get it to that same state that the bug occurs in to able to fix it. This is second nature to many of us since we have done it so many times.
React takes a fundamentally different approach but it takes some retraining of your brain. However I argue this approach is superior due to it being much easier to reason about, making it more readable for newcomers to the code and much more debuggable. In React, you think of your UI as snapshots. You do not think of it as a progression of time and events; rather, you look at the UI as how is it going to look given a set of parameters. That’s all it is. Given a set of parameters, how does this UI look? Using the drop down example, you think of the drop down in its various states: an open state, a closed state, and an event that triggers when you click the item. You represent these varying states using props and state (we’ll get to state in a bit.) Given a certain set of props, the UI always looks this way. This will become more concrete as we go on.
This brings me to my next point: when coding React, assume you have all the data you need coming in via props and then figure out later how to get it there. That will make it much easier. Just assume it all works and then later go make it work.
And these principles? Not invented by React. These are battle-tested ideas that stem a lot from functional programming. There’s a lot of good computer science going on here, whether or not we use React to apply these concepts.
Okay, great, let’s go to Search and drop in our new component.
Much like you give an HTML tag an attribute is how you give props to children components in React. Here we’re passing down an object to our child component to make it available to the ShowCard via props. Neat, right? Save it and reload the page. standard is going to give you a bunch of complaints but we’re going to address that momentarily. You should see the same UI.
We give it a key so React can internally keep track of which component is which. We give it an id to refer to later.
So let’s fix our standard errors now. standard-react dictates that all props have a propType. React has a features that allows you to set propTypes which it then validates at runtime. This ends up being great for debugging because React now knows what sort of props it should be getting so it can give you a meaningful error messages. So let’s go fix the errors.
In ShowCard, go add this just below the declaration of the ShowCard function:
Now React knows to expect that show is both an object and required for the ShowCard to work. If the prop is optional (which is fine if it is indeed optional) then leave off the isRequired part.
We can make this a little neater via the ES6/JSX spread operator. Let’s try that. Change Search’s ShowCard from <ShowCard show={show} />
to <ShowCard {...show} />
. This will take all the properties from show and spread them out as individual properties on ShowCard. You could write <ShowCard title={show.title} poster={show.poster} description={show.description} year={show.year} />
but that’s a lot of writing and this cuts an easy corner. Let’s go modify ShowCard to match.
We’ve now made our code a bit cleaner since we don’t have to props.show… ad nauseam. We’ve also made it so React can check each individual property that we need; this will save us bugs in the future.
React Router Layout
Now, we have a common layout that we want to maintain between all of our pages. This is a common problem: you make a nice looking nav bar and background that you intend to share amongst multiple pages. We could make a nav component and share that but we can take it a step further (right now we’re just going to share the background div.) We’re going to use nested routes and what’s called an IndexRoute. A nested route allows you to share UI between routes and an IndexRoute is just the default, base route of a nested route. Let’s see it.
Make a new file called Layout.jsx. Put:
In ClientApp, pull in the IndexRoute component from react-router and make a nested route in your component.
children
is in particular an interesting beast. It allows you to make a tag and have access to whatever’s inside. So:
The children here would be the div and the h1. That’s what children get you.
Also you may be seeing PropType errors here. Those are React’s friendly ways of reminding you to make sure you’re getting the data you expect via props.
React: State
We’ve discussed props which all you to have immutable state passed from parents to children. However, as any seasoned UI developer will point out, interfaces are inherently stateful. You app at some level must contain some level of mutability. React gives you a very controlled window to introduce this mutability to be able to reason easily about this mutability aptly called state.
While props are passed down from parents and are immutable, state is created, read, and mutated all inside of a component. In other words, if a component has state, that state cannot be mutated by a parent, child, or any other external influence; only that same component has access to the setState method which is the only way to mutate state. That component has the ability to expose methods to children that the child can call to let the parent know it should mutate its state, but again, it is totally up to the parent to respect that call and mutate the state; the child can only call methods exposed to it via passed-down props.
So let’s see this in action. We’re going to add a header that allows us to search our shows.
In Search.jsx, add the following:
Now the UI is in place. Let’s start tracking what’s actually in the input. But in order to do that, we need to change a bit about our component.
So far we’ve exclusively been using what are called “stateless function components.” This are awesome because they are the simplest form of a component. Also keep in mind these are relatively new to React (v0.14+). Now we’re going to convert this to what’s called the ES6 class syntax. Underneath I’ll throw the older “createClass” syntax so you can compare; either works and some people prefer one over the other. I’ll let you be the judge.
They each have their advantages that I won’t discuss at length. I’m going to be using the ES6 one from here on out so I suggest you do that (or else you’ll have to translate the examples.) And you can mix-and-match the different syntaxes too.
After switching to this syntax, your code should still work, but now we can hook into other features that React components have. Namely we’re going to add state. Let’s do that now.
Notice the constructor accepts props and passes that up to React via super(props)
. A necessary evil of boilerplate, I’m afraid. Anytime you have initial state of any sort you need to put that line in. Luckily if you forget, React has a friendly runtime error message to remind you.
I replaced the brand momentarily so you can see the see the searchTerm change. You should see whatever you made the initial state for searchTerm show up as the brand. Neat, right? Alright, let’s make it mutable now. Change the input in the header to be this:
Cool! Now your input should have the initial state of your searchTerm. Now try and type and/or delete anything. You can’t! You broke the Internet! Just kidding. But to understand why this weird bug is happening you have to understand how React handles keypresses. Your state object on your component states that the value of searchTerm is 'this is the default searchTerm'
. When a keypress happens, React kicks off a re-render. Since nothing modified the value of searchTerm, it’s still the same string and thus it re-renders the same string there. Your state object is the source of truth. So let’s make the value of searchTerm bound to the value of the input.
Now try typing in the input. As you see, the title is now reflective of whatever you type in the search input. So let’s chat about what we did here. We made an event listener that handles the change events that the input throws off when it has a keypress. That event listener accepts an event that’s technically a React synthetic event but its API looks just like a normal DOM event. In the event listener, we call this.setState
, a method that allows you to mutate the state and then lets React re-render. If you don’t call setState and instead mutate this.state
yourself, React isn’t privy to the fact the fact that you’re changing stuff and thus doesn’t know to re-render. In other words, never modify this.state
directly and always just use setState. setState works like Object.assign
in that it will do a merge of your objects (it’s a shallow merge and not a deep merge) so you’re free to just modify the keys you need to. Finally, in the constructor we added a line to bind the correct context of the event listener since we need to ensure that handleSearchTermChange is always called with the correct context.
So go back now and change the brand to the correct title.
Let’s make the search actually do something now. Since now we have our state being tracked, let’s use it do a real time search on our titles.
This is a little clever but let’s dissect the new filter line we added. We’re looking at both the title and description lines to search on and using the indexOf method from strings to see if the searchTerm exists within the description or title. We use toUpperCase on both to make it case agnostic. And the filter method on arrays just filters out items in an array that the method returns false on. Now try typing in your searchBox. You should see it filter as you type. We could make this more efficient but I’ll leave that to you in your own time.
Testing React with Mocha, Chai, and Enzyme
So now we have a component worth testing so let’s do that. We’re going to be using the old standbys Mocha and Chai. These principles will generally apply to AVA or Jasmine too but I rolled with Mocha due to the fact that I’ve used it for so long that it’s easy for me teach.
We’re also going to be using a testing helper from our friends at Airbnb called Enzyme. Enzyme is just some helpers to make testing React components easier. You don’t have to use it; the React testing utils are great too (and you can use both of them at the same time too.)
So, first things first. Create a directory called test. In our test environment, we need a few things to happen. We need JSX/ES6 transpilation to happen or our tests will crash on the unfamiliar syntax. We also need a fake DOM for React to interact with which we’ll get from a library called jsdom.
So in your test directory, create another directory called helpers (these files are automatically excluded from being run as tests by Mocha) and create a file called setup.js. In setup.js, put
This little setup is totally lifted from Kent C. Dodds. Thanks Kent.
Now that we have our environment set up, let’s get our tests set up as an npm script. Add the following to your scripts object in your package.json
You can add -R nyan
to the end for fun too. This changes the reporter to be the Nyan Cat and I just can’t help my self. There are other reporters. The --require
part just makes sure that our setup gets run first before our specs too. I tend to leave it off since it doesn’t report the tests that do pass and that’s so satisfying for me
Great! Let’s start writing some tests. For this workshop we’re just going to shove all our tests into one spec but I encourage you to do split them up into different files as appropriately split-up files. Create a new file called App.spec.js. The .spec.js convention is just to let your future self / other coders know that this file corresponds to the whole app. In this case it’s not significant what it’s called (the naming is significant in other testing frameworks.)
In your new file, put:
The first line is to let eslint know that this file is run through Mocha and thus a certain of globals made available by Mocha are okay to use (like describe and it.) You’ll have to put this pragma at the beginning of all your tests so it won’t fail lint. Next we’re requiring Chai which is our assertions library. I like the expect way of doing tests but you’re okay to use assert or should. And then we’re just setting up a stupid test to make sure it’s all working. Run your test and you should see one test passing.
Now that our test is passing, let’s make some real tests. Let’s test to make sure that our branding is always showing up. Because branding is important, right? Delete our bogus test and put:
So now we’re pulling in Enzyme and using its shallow-rendering ability. This means it will render everything except its child composite component. In this case, the only composite components are the ShowCards so it won’t reach down into the ShowCards to render them; just stubs. This is sufficient to test if the branding is there. A neat trick is to put console.log(wrapper.debug())
. That will show you what it’s dealing with. Let’s add another more useful test.
Here we’re testing to see if all the initial ShowCards are getting rendered. There should be a ShowCard for every show in the preload. Let’s add one more to test the search functionality.
Since we’re now interacting with the App programmatically, we have to use the much slower and expensive mount which will use jsdom to simulate a real DOM. We’re going to change the input and make sure the state changes and that the search works the way we expect by narrowing it down to the two pertinent results.
Cool! Now we have some unit tests in place. Now let’s talk about my theory around unit testing and React: I never write tests like this. I don’t test my UI code ever. My UI is ever shifting and in reality, I don’t much care if my markup changes. I expect to as we design and iterate on the website. However I do test the hell out of my business logic which I’ll separate into small modules. In any case, I’ve shown you how to test your React and I’ll let you decide.
istanbul / nyc
A good-but-not-great metric of how well you’re testing your is code coverage. Basically it’s how much of your code gets covered by your tests. There exist many tools to do this but we’re going to use the most common in the JavaScript world: istanbul. Well, sort of. We’re going to be using nyc which is built on top of istanbul but I find it a bit easier to work with.
What istanbul is going to do? Istanbul instruments your code to see what code gets run and then lets you when your tests are covering and/or not covering other parts. So let’s implement that as an npm script. Add:
Now if you run npm run cover
you should see 100% coverage on both Search and ShowCard. That means all the exported code is getting covered in a test! Yay! But where are the rest of our files? Well, we’re not testing them at all yet so istanbul doesn’t know about them. Once you start testing those files they’ll show up.
Now try adding an extra method to Search. It can be some dumb method that returns 5. Now run your npm run cover
again. Your code will not be totally covered now. This is what istanbul gets you. If you open coverage/index.html in your browser, you’ll actually be able to visualize what the code coverage looks like. Pretty sweet.
A Note on Hot Module Reload
So webpack has a nifty ability to do what’s called hot module reload (HMR.) If you’ve ever used LiveReload’s CSS injection, this will sound familiar. HMR will take your code, compile it on the fly, and then inject it into your live-running code. Pretty cool tech.
If you’re working a dropdown that requires three different clicks to get there, it’s pretty neat to be able to change the code and watch the UI change without having to reload and get the UI back into a state where you can see the effects of your change.
But we’re not going to do it today. For one, HMR does not work with our stateless functional components at all. So we’d have to convert everything to classes which is a burden. It also just requires a lot of setup that can be finicky at times. The author himself says the tech is a (great) hack.
So suffice to say, I want you to be aware of its existence and continuing evolution but I feel like our time is better off investigating other parts of the React ecosystem.
Data in React
So now we want to add a third page: a page where we can watch the trailer. This is going to be an application of what we know already.
Create a new file in js/ called Details.jsx. In Details put:
In ClientApp.jsx, put your new route:
Here we’ve added a URL parameter using the colon. Now as a prop to Details you’ll get whatever :id was. So now try to manually change the anchor on your url to be [stuff]/index.html#/details/1
. You should see your new component here.
Let’s show you a neat debugging tip I totally stole from Ryan Florence. replace that h1 with this:
This is a useful way to dump your params to the page. This is awesome for state too; it shows you in real time what your state looks like. We’ll dig into React Tools here in a sec for even better debugging but for now let’s keep trucking with our Details.jsx
We’re going to show all the details of a show on this page and be able to play the trailer. There’s a big problem here that we don’t have that data on this page though; it’s available in the Search route. We could require in data.json here given that our data is available that way but that typically isn’t the case: we typically get this data from the server. If that’s the case, you don’t want to make two AJAX requests to get the same data. In other words, we need to share this state between components. The way you do this is by pushing up the state to the highest common ancestor component. In this case, that’d be the router in ClientApp. So let’s first refactor Search to still work while it pulls in that data from Search.
Now make Search use it
Cool. Now it should still work but Search no longer controls the data but merely receives it as props. Now we can share this data with Details.
Now we’re going to pass the correct show to the Details page. There’s a bunch of ways to do this:
- We could pass all the shows and let Details select the correct show. This isn’t great because Details is given an additional concern it doesn’t need to have.
- We could create a callback in ClientApp that it passes to Details that Details calls with the correct ID and ClientApp hands back the correct show. This is slightly better but it’s an odd API for getting data. Ideally we just hand down props and not a callback, especially since this isn’t async.
- Or we could hook into react-router’s onEnter callback for the route, grab the ID, and then pass that down in addition to the ID to Details. This is my preferred approach. So let’s do that.
Add the following to ClientApp:
This should put the correct show as one of the props.params that ClientApp passes down to Details. If you refresh the page, you should see it now.
As an aside, I’ve found the best way to organize React method component is the following
- getInitialState / constructor
- Other lifecycle methods like componentDidUpdate (we’ll talk about those in a sec)
- Your methods you create (like assignShow)
- render
Makes it easier to find things when you look for them.
So let’s actually display some cool stuff:
Now you should have some nice looking UI.
Well, now we have a header in two places. Let’s abstract that in a component and use that in both places. Create a new file called Header.jsx and put this in there:
We’re even going to throw in a link back to the home page for fun. Now open Details.jsx and put:
Let’s put a back button on the Header so you can get back to Search after you reach it.
So let’s integrate this to Search. But it’s not so simple since on Search we want the header to have a search input and on Details we want a back button. So let’s see how to do that. In Header.jsx put:
In Search.jsx:
This is how you have a child component modify a parent’s state: you pass down the callback and let it call the parent to let the parent modify the state. This also demonstrates how to conditionally show one component and not another.
Lastly let’s make our show cards clickable.
React Tools
So far we’ve been using pretty old school debugging technology: console.logs and just dumping stuff out to the DOM. There is an easier way! React Dev Tools! Grab it here for Chrome and Firefox. In theory you can get the Chrome version working on Microsoft Edge, but good luck. If you’re not using Chrome or Firefox, you’re out of luck for now. They’re talking about doing a standalone app but we’ll see when that finally surfaces. I’ll be talking about the Chrome version because that’s the one I know the best.
Dev Tools allow you to explore the virtual DOM of React as if it was just a normal DOM tree. You can see what props and states are in each component and even modify the state of components. The virtual DOM explorer is by-far the most useful part of it.
Find the Dev Tools in Chrome by opening the Chrome dev tools and the last tab (along side Console, Sources, Network, etc.) should be React on your page. If you don’t see it try restarting your browser. If you still don’t see it, the tab won’t show up if the extension can’t detect React on the page. You may have a dated version of the dev tools. After that I’m not sure; it can be fickle sometimes.
Feel free to poke around a bit here to familiarize yourself with what the React Dev Tools can do. Here are a couple of tricks for you:
- If you right-click -> Inspect Element on something and then click the React tab, it will take you straight to that element in the virtual DOM.
- Select something in the virtual DOM in the React tab. Now go to the Console tab and type
$r
. It should be a reference to the element you have selected in the virtual DOM and you can manipulate it. - As a side note, you can do the above trick with the normal DOM explorer with
$0
,$1
,$2
,$3
, and$4
instead where 0 is the last one you selected, 1 is the penultimate, etc. - iframes and Chrome extensions don’t work with React Dev Tools as of writing.
redux
The next thing we want to do with our app is make the front page’s search work so that when you type in a search query and hit enter it will automatically have searched for that on the Search page. Right now you have all the necessary tools to do that via state. You could just push the query term up to the ClientApp level and then pass that down to the Search and you’d be done. And that’s probably the way you should do it given how small our app is.
But when these demo apps all the fun is in over engineering it and that’s precisely what we’re going to do: we’re going to add redux. redux is a fantastic tool and a cool blending of the ideas of Facebook’s Flux and the Elm architecture. If you’re going to use a Flux-like architecture in your app, make it redux.
So what is redux? Redux is a predictable state container for JavaScript apps. The best part about it while the concept is at first hard, I’d argue it’s also very simple and elegant. redux is great because it will run both client and server side, it’s easy to test, and easy to debug. While redux does not not follow the Flux pattern, you can easily see the similarities and once you’ve done one the other isn’t hard to adapt to.
With redux you a single store which stores your entire app state in a single tree. This is not like Flux where you’ll have many stores for many different parts of your app; all data lives in a single store. You cannot directly modify the tree of data stored in this tree by typical assignment (ie tree.prop = 'foo'
doesn’t work.) Rather, every time you want to modify the tree, you emit an action. Your action then kicks off what’s called a reducer. A reducer is a special function that take a tree and parameter(s) and returns a new tree after applying whatever transformations it deems fit. The way it gets away with just one store is when you need more data you just add more branches to your data tree. Like React? You only have one tree of components and when you need more you just add more nodes (branches) to your components.
So let’s do the most basic addition of redux to our app and convert the Search to use redux. Again, this is using a sledgehammer to solve a tiny nail problem: huge overkill.
Create Store.jsx. Because our use case is so small, I’m going to shove all our concerns in here. You should separate these out. But not today!
Here we’re doing everything to bootstrap a redux store. You can see our root reducer function there. With the root reducer, you’ll take in an action and then using a switch statement on the action type, you’ll delegate that action to another reducer. If necessary, that reducer can delegate to yet another reducer and so on (I haven’t had a compelling reason to do that yet.) Right now we don’t have a reason to do so that we’re just doing everything in the root reducer. You could change the body of the case SET_SEARCH_TERM:
to be another reducer function.
Then to create a new store you just give redux the root reducer and it’s created! Cool!
From there we’re going to create a connector. react-redux is a library that provides some simple helpers to connect your redux store to your React app. Everything that react-redux does for you you can fairly easily do for yourself. For that matter, redux itself is a pretty simple library that would possible to write yourself (unlike React; good luck with that!) We’re creating a mapStateToProps and a mapDispatchProps to props which are just helpers that will hand your pertinent components with the methods and state they’ll need to be able to display it. Now whatever components your wrap with connector will have these bits of state and action creators available to them. We’ll see how that works in a sec.
Let’s do some connecting with ClientApp.jsx
Wrapping our app in a Provider
component makes our store and dispatches available where-ever we wrap components with connector calls, hence why it’s necessary to wrap the whole app. Good news is once you do this it magically just works thought out your whole app. You’ll only have to use
once.
Let’s make it work with Header so it correctly displays and changes the redux store.
Now you should be able to type in the header and see it immediately reflected. The search part doesn’t work yet so let’s go over to Search and make it reflected in the UI.
Deleting code is always awesome. Since we’ve externalize a lot of this we don’t need it here anymore. Then we change the filter to rely on the props instead of the state and make sure the component is wrapped. That’s it! Now the whole system should work.
Let’s make the front page work. Go to Landing.jsx
There you have it! Now you should be able to search from the Landing page! If you hit enter on the form, it should search for you. As a cool side-effect, now that state will be perpetuated from screen to screen. If you type ‘dare’ into the search, click into the Daredevil show and then click back you should see ‘dare’ still in the search.
redux Devtools
One of the most compelling reason to use redux is its amazing debugging experience. redux has the fantastic ability to do time-traveling debugging, meaning you can step forwards and backwards through actions you’ve dispatched. It’s really powerful for debugging.
There are several ways to get it working but I’m going to show you the bare minimum to get up and running. Unlike React, there is some code you have to put in to get it working. Luckily, it’s like two or three lines.
In Store.jsx put:
That’s it for code! It just adds a middleware to redux that hooks into the dev tools. It’s also doing a window check to make sure if you’re unit testing or running your components in node that the window reference doesn’t blow up.
Now go grab the Chrome extension. The Firefox one is forthcoming as is the Safari one. Until then Chrome is it. Good news is that you can actually just build the debugger into the page so it works everywhere. Bad news is I’m going to show you how since I’ve never done it. In any case, feel free to explore it on your time.
Okay, last bit: this doesn’t work with the file:///
protocol. So we’re going to use node’s http-server. If you don’t have it already just run npm install -g http-server
. From there, navigate to your project’s root directory and run http-server -p 5050 ./
. Then open localhost:5050 in your browser.
Now you should see three green circles with smaller orange circles circling the green ones on your Chrome tool bar. Click on that and that should show you the redux tools. This allows you to play with the redux tools. I’ll let you toy with them but suffice to say they’re pretty impressive.
Fixing Our Tests and Testing redux
So we broke all of our tests. They all fail now. High five! This is a big reason why I’m hesitant to test UI code: I find my tests break all the time just because I’m rewriting markup or other code. Nonetheless, let’s refix our tests and add two for redux. As opposed to testing React which I don’t do much of, I test the hell out of my redux code. redux code is very testable and you should cover all or nearly-all of your reducers with tests.
Cool, open our spec doc. Let’s fix these tests one-at-a-time. For the last two tests, change their method calls from it( ... )
to xit( ... )
. This will prevent Mocha from running them so we can work on them one-at-a-time.
The first test is actually testing Header so let’s move it to its own describe too.
So because of all the misdirection and wrapping components that accompany the react-redux bindings, we lost our ability to shallow render. As such, we have to fallback to our jsdom, render version. This is much slower. You can take the time to unwrap all the pieces to make the shallow version work; we’re just not going to do it here.
Here we don’t need to wrap <Header /> in a
Let’s move on to our first
We have to mock what react-router would give to the Search route. Other than than that, this look pretty familiar. We have to move from the friendly enzyme wrapper to the less-friendly Cheerio wrapper.
Let’s rewrite our last test.
Here we’re passing the state already into the component. Make sure the dispatch comes before the render so that the component will have the correct state when it goes to render.
Testing redux
Let’s add two more tests to show how to test redux. First we need to add a new line to Store because we really want to test our reducers.
Now go back to App.spec
Great, so now we know our reducer creates our app the way we want to. Let’s make sure that the setSearchTerm action works too.
That’s it! Reducers are easy to test due to their functional nature. Something key with reducers is that they are pure functions. Means that they don’t modify any state around them (including the params you pass in.) They only perform a transformation and return a new items.
Universal Rendering
Universal rendering, or the artist formerly known as isomorphic rendering. The idea here is that you server-side prerender your code so that when it gets down to the client, your browser can instantly show the markup while your app bootstraps in the background. It makes everything feel very instantaneous.
With just vanilla React, universal rendering is a cinch. Check out the whole node file from another one of my workshops. It does server-side rendering in just a few lines.
It’s not quite so simple now that we have routing involved. We don’t want to have to duplicate all of our routing info that we wrote for react-router. Rather, if possible, we just want to reuse the routes we already built for react-router. So let’s do that (with some refactoring.)
Move shows to redux
We’re passing shows everywhere. In order to simplify our app a lot by not having to worry about passing it in, let’s make all the shows-based data just come from redux.
Go to Store.jsx
Cool. Now these shows will available to any connected components. Let’s go fix Details first.
This should fix Details. Let’s fix Search.
Just needed pull shows from a different part of the params. Not much else to change here. Now (while your tests and lint are failing at the moment) the app should still work but the shows are coming from redux. Let’s go clean up ClientApp.jsx
Feels good to delete code. We successfully simplified our app a lot by moving that to redux. And now it will simplify us moving to render universally. Let’s fix our test too.
All tests should pass now. Now we have a few more things to refactor before we can universally render successfully.
Split out BrowserEntry.jsx
The big key with universal rendering is being careful about referencing window
and document
as those aren’t available in node environments. That isn’t to say you can’t interact with them: you just have to do if (window) { /* do stuff with window*/ }
. Part of that means we need to split out the rendering of ClientApp from the declaration of the component. Remove the ReactDOM stuff from ClientApp, create a new called BrowserLanding.jsx and put this in there:
We’re also going to be doing a couple of things with history here. First, we’re switching from hashHistory to browserHistory. Instead of localhost:5050/#/search
it’s just going to be localhost:5050/search
which we can do since we’re doing server-side rendering and can control the routes.
Go back to ClientApp.jsx
And now the webpack config
Cool. That should cover our refactor to split out BrowserEntry. Now we’ve done that, let’s refactor a bit more: split out routes.
Back in ClientApp
Now we can pass these routes into the server to be able to reuse them. This should wrap up all the things we need to refactor in our client code and now we spew out our server code.
Get your HTML ready
Quickly in your index.html file go add the following:
Just adding that body template tag will allow to render our React string directly in there. Pretty cool.
Writing the Server
This is a lot to take in at once but we have to write the whole app at once. Reason being is that you need it all to be able to run the server. So let’s write it first and then deconstruct it.
So we require a bunch of stuff and build a basic Express server. From there we’re going to use react-router’s match function which is what react-router uses internally to match routes. We pass that route to match as well as the URL. From there, we first check for 500s, then for 300s, and then if there was a matched route. If none of those are matched, then we throw off a 404.
Once a route is matched, we use ReactDOMServer to render our app out to a string (instead of to the DOM.) Once we have that string, we use lodash to template our rendered string into the index.html markup. And that’s it! You’re universally rendering!
React Lifecycle Methods and AJAX with React
Due to the structuring of our app, we haven’t had to use React lifecycle methods despite the fact they’re fairly common to use and thus important to know. One of the most compelling reasons to use lifecycle methods is to do AJAX. Once a component gets mounted to the page then we want to be able request data from the server. First let’s discuss the lifecycle of a React component.
- constructor/getInitialState - This is where set up your components initial state. The former is for ES6 classes (that we’ve been using) and the latter is for the React.createClass method.
- componentWillMount - This method runs right before the component gets mounted. This one is not too common to use, but you will want to use it any time you want code to run both in node and in the browser.
- componentDidMount - This method runs right after your component gets put into the DOM. This method will not get run in node but will in the browser. This makes it so your component can render first then you can go get the data you need. In your component you can throw up a loader if you need to. Also if you need to interact with the DOM (like if you were wrapping D3 or a jQuery plugin) this would be the place to do it.
- componentWillUnmount - This method runs right before the component is taken off the DOM. Most common thing to do here is get rid of external event listeners or other things you need to clean up.
Cool! So let’s make our Details page check the IMDB rating! Open Details.jsx
We’re requiring in axios which is a great little promise-based AJAX client and using that to make requests to the Open Movie Database to find the IMDB ratings. If you go to your pages now you’ll notice that the rating is showing up a little after the page renders. That’s because it’s being grabbed from the magical Internet tubes! As you can see, we did this componentDidMount so that the user could see UI before waiting on an AJAX request. Notice that it doesn’t get server-side rendered either because the server doesn’t call componentDidMount.
That’s it! That’s all you need to need know about AJAX with React as well as the lifecycle methods!
webpack Chunking and Async Routing
So far all of our routing with react-router has synchronous which makes sense. When we detect that a user has requested a route, we already have that route in our bundle.js and we render and serve that to them. The logic follows.
However, as our app grows and grows, our bundle.js is going to get bigger and bigger in file size. Wouldn’t it be better if you were on Search that it served you _just the JavaScript you need for that page and none of the JS for Landing or Details? For example, Search doesn’t need the omdb client we brought in: that client can safely just be loaded on the Details page.
Enter webpack’s code splitting ability. It’s smart enough to know which files are required by which other files and thus if you choose to use webpack’s async loading API (require.ensure
) then webpack will automatically start chunking your JS for. What’s more is we don’t have to write the glue code that will download the chunks as we need them: webpack just does this for us. All we have to do is identify the modules that can be async by treating them as if they were. Really cool.
So we’re going to treat all of our routes as async and luckily react-router is already instrumented for this for both server and client-side. So let’s go make it happen!
So first we need give a minor tweak to webpack.config.js.
First we need to tell webpack where to find all of its bundles instead when it calls back to the server to grab them. We also would like to see some webpack stats now on chunks since we’re using them.
Go to ClientApp
First bit is the async API we were talking about for chunking, require.ensure. This API lets webpack that it should wait and lazily load this component as soon as ensure is called. This API is available in the webpack, client-side world as it’s available as a part of webpack’s packaging; however, require.ensure is not necessarily in node and thus this shim is needed.
Next, we’re eschewing from the JSX-ish config of react-route to the object config style. We could have done this before but now we have to because this is the only way to async route loading. Notice the getComponent is a function now which means we can lazy load this routes as we need them. webpack is smart enough to call that function, wait ‘til it gets downloaded and then keep going. We’re not going to do it today, but in the route that’s being transitioned away from you can throw up a loading state while it loads the new code.
Next we move the config of routes from the children of Router to just being that object we created about.
And below that we need to ship out the routes so they can be universally rendered and the browserHistory because BrowserEntry is going to need it. So let’s go to BrowserEntry
The match looks similar to what we do on the server. What we do here is make sure that the routes get check synchronously initially so that we can make sure that React renders correctly the first time so it matches the server-rendered code or otherwise React will blow away your server-rendered code in favor of its own.
Go to app.js
Only difference is that routes is no longer a function invocation but rather just an object of configs so all we do is remove the parens. And that’s it! The rest should be handled by webpack and react-router.
So now browse around your site and watch the files being loaded in via the dev tools network tab. You should see the chunks being loaded as they are needed. This again doesn’t split out too much since this is such a small app but you know how to do it! It’s not as hard as one may expect!