├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── _config.yml ├── _includes ├── footer.html ├── head.html ├── header.html ├── icon-github.html ├── icon-github.svg ├── icon-twitter.html └── icon-twitter.svg ├── _layouts ├── default.html ├── page.html └── post.html ├── _posts ├── 2016-01-01-intro.markdown ├── 2016-01-02-setup.markdown ├── 2016-01-03-react.markdown ├── 2016-01-04-tooling.markdown ├── 2016-01-05-tooling-2.markdown ├── 2016-01-06-jsx.markdown ├── 2016-01-07-react-2.markdown ├── 2016-01-08-react-router.markdown ├── 2016-01-09-react-props.markdown ├── 2016-01-10-react-state.markdown ├── 2016-01-11-react-testing.markdown ├── 2016-01-12-istanbul.markdown ├── 2016-01-13-hot-module-reload.markdown ├── 2016-01-14-react-marshall-data.markdown ├── 2016-01-15-react-tools.markdown ├── 2016-01-16-redux.markdown ├── 2016-01-17-redux-devtools.markdown ├── 2016-01-18-fixing-tests.markdown ├── 2016-01-19-universal-rendering.markdown ├── 2016-01-20-ajax-and-lifecycle.markdown └── 2016-01-21-webpack-chunking.markdown ├── _sass ├── _base.scss ├── _layout.scss └── _syntax-highlighting.scss ├── about.md ├── css └── main.scss ├── feed.xml └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-metadata 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gem 'github-pages' -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | RedCloth (4.2.9) 5 | activesupport (4.2.5.1) 6 | i18n (~> 0.7) 7 | json (~> 1.7, >= 1.7.7) 8 | minitest (~> 5.1) 9 | thread_safe (~> 0.3, >= 0.3.4) 10 | tzinfo (~> 1.1) 11 | addressable (2.3.8) 12 | coffee-script (2.4.1) 13 | coffee-script-source 14 | execjs 15 | coffee-script-source (1.10.0) 16 | colorator (0.1) 17 | ethon (0.8.1) 18 | ffi (>= 1.3.0) 19 | execjs (2.6.0) 20 | faraday (0.9.2) 21 | multipart-post (>= 1.2, < 3) 22 | ffi (1.9.10) 23 | gemoji (2.1.0) 24 | github-pages (48) 25 | RedCloth (= 4.2.9) 26 | github-pages-health-check (= 0.6.1) 27 | jekyll (= 3.0.3) 28 | jekyll-coffeescript (= 1.0.1) 29 | jekyll-feed (= 0.3.1) 30 | jekyll-gist (= 1.4.0) 31 | jekyll-mentions (= 1.0.0) 32 | jekyll-paginate (= 1.1.0) 33 | jekyll-redirect-from (= 0.9.1) 34 | jekyll-sass-converter (= 1.3.0) 35 | jekyll-seo-tag (= 1.0.0) 36 | jekyll-sitemap (= 0.10.0) 37 | jekyll-textile-converter (= 0.1.0) 38 | jemoji (= 0.5.1) 39 | kramdown (= 1.9.0) 40 | liquid (= 3.0.6) 41 | mercenary (~> 0.3) 42 | rdiscount (= 2.1.8) 43 | redcarpet (= 3.3.3) 44 | rouge (= 1.10.1) 45 | terminal-table (~> 1.4) 46 | github-pages-health-check (0.6.1) 47 | addressable (~> 2.3) 48 | net-dns (~> 0.8) 49 | public_suffix (~> 1.4) 50 | typhoeus (~> 0.7) 51 | html-pipeline (2.3.0) 52 | activesupport (>= 2, < 5) 53 | nokogiri (>= 1.4) 54 | i18n (0.7.0) 55 | jekyll (3.0.3) 56 | colorator (~> 0.1) 57 | jekyll-sass-converter (~> 1.0) 58 | jekyll-watch (~> 1.1) 59 | kramdown (~> 1.3) 60 | liquid (~> 3.0) 61 | mercenary (~> 0.3.3) 62 | rouge (~> 1.7) 63 | safe_yaml (~> 1.0) 64 | jekyll-coffeescript (1.0.1) 65 | coffee-script (~> 2.2) 66 | jekyll-feed (0.3.1) 67 | jekyll-gist (1.4.0) 68 | octokit (~> 4.2) 69 | jekyll-mentions (1.0.0) 70 | html-pipeline (~> 2.2) 71 | jekyll (~> 3.0) 72 | jekyll-paginate (1.1.0) 73 | jekyll-redirect-from (0.9.1) 74 | jekyll (>= 2.0) 75 | jekyll-sass-converter (1.3.0) 76 | sass (~> 3.2) 77 | jekyll-seo-tag (1.0.0) 78 | jekyll (>= 2.0) 79 | jekyll-sitemap (0.10.0) 80 | jekyll-textile-converter (0.1.0) 81 | RedCloth (~> 4.0) 82 | jekyll-watch (1.3.1) 83 | listen (~> 3.0) 84 | jemoji (0.5.1) 85 | gemoji (~> 2.0) 86 | html-pipeline (~> 2.2) 87 | jekyll (>= 2.0) 88 | json (1.8.3) 89 | kramdown (1.9.0) 90 | liquid (3.0.6) 91 | listen (3.0.6) 92 | rb-fsevent (>= 0.9.3) 93 | rb-inotify (>= 0.9.7) 94 | mercenary (0.3.5) 95 | mini_portile2 (2.0.0) 96 | minitest (5.8.4) 97 | multipart-post (2.0.0) 98 | net-dns (0.8.0) 99 | nokogiri (1.6.7.2) 100 | mini_portile2 (~> 2.0.0.rc2) 101 | octokit (4.2.0) 102 | sawyer (~> 0.6.0, >= 0.5.3) 103 | public_suffix (1.5.3) 104 | rb-fsevent (0.9.7) 105 | rb-inotify (0.9.7) 106 | ffi (>= 0.5.0) 107 | rdiscount (2.1.8) 108 | redcarpet (3.3.3) 109 | rouge (1.10.1) 110 | safe_yaml (1.0.4) 111 | sass (3.4.21) 112 | sawyer (0.6.0) 113 | addressable (~> 2.3.5) 114 | faraday (~> 0.8, < 0.10) 115 | terminal-table (1.5.2) 116 | thread_safe (0.3.5) 117 | typhoeus (0.8.0) 118 | ethon (>= 0.8.0) 119 | tzinfo (1.2.2) 120 | thread_safe (~> 0.1) 121 | 122 | PLATFORMS 123 | ruby 124 | 125 | DEPENDENCIES 126 | github-pages 127 | 128 | BUNDLED WITH 129 | 1.11.2 130 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
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.
18 |
19 | ## npm global installs
20 |
21 | Run the following global npm installs
22 |
23 | {% highlight bash %}
24 | npm install -g mocha
25 | npm install -g nodemon
26 | npm install -g webpack
27 | npm install -g standard
28 | {% endhighlight %}
29 |
30 | We'll be using webpack v1.12, Mocha v2.4 and standard v6.0.
--------------------------------------------------------------------------------
/_posts/2016-01-03-react.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "React"
3 | ---
4 |
5 | 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.
6 |
7 | 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.
8 |
9 | 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.
10 |
11 | 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.
12 |
13 | ## My First Component!
14 |
15 | So let's start our first React code!
16 |
17 | {% highlight html %}
18 |
19 |
20 |
21 |
22 | <​script src="js/ClientApp.js">
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
49 |
50 | {% highlight javascript %}
51 | /* global React ReactDOM */
52 |
53 | var div = React.DOM.div
54 | var h1 = React.DOM.h1
55 |
56 | var MyTitle = React.createClass({
57 | render () {
58 | return (
59 | div(null,
60 | h1(null, 'Check out this component!')
61 | )
62 | )
63 | }
64 | })
65 |
66 | var MyFirstComponent = (
67 | div(null,
68 | React.createElement(MyTitle, null),
69 | React.createElement(MyTitle, null),
70 | React.createElement(MyTitle, null)
71 | )
72 | )
73 |
74 | ReactDOM.render(MyFirstComponent, document.getElementById('app'))
75 |
76 | {% endhighlight %}
77 |
78 | Cool, right!
79 |
80 | `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.
81 |
82 | 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`.
83 |
84 | 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!
85 |
86 | 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.
87 |
88 |
89 | ## Factories
90 |
91 | This is a bit verbose to write React.createElement
so many damn times. Let's use a shortcut.
92 |
93 | {% highlight javascript %}
94 | // replace MyFirstComponent
95 | var MyTitleFact = React.createFactory(MyTitle)
96 |
97 | var MyFirstComponent = (
98 | div(null,
99 | MyTitleFact(null),
100 | MyTitleFact(null),
101 | MyTitleFact(null)
102 | )
103 | )
104 | {% endhighlight %}
105 |
106 | 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.
107 |
108 | ## Props
109 |
110 | 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.
111 |
112 | {% highlight javascript %}
113 | /* global React ReactDOM */
114 |
115 | var div = React.DOM.div
116 | var h1 = React.DOM.h1
117 |
118 | var MyTitle = React.createClass({
119 | render () {
120 | return (
121 | div(null,
122 | h1(null, this.props.title)
123 | )
124 | )
125 | }
126 | })
127 |
128 | var MyTitleFact = React.createFactory(MyTitle)
129 |
130 | var MyFirstComponent = (
131 | div(null,
132 | MyTitleFact({title: 'Props are great!'}),
133 | MyTitleFact({title: 'Use props everywhere!'}),
134 | MyTitleFact({title: 'Props are the best!'})
135 | )
136 | )
137 |
138 | ReactDOM.render(MyFirstComponent, document.getElementById('app'))
139 | {% endhighlight %}
140 |
141 | 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.)
142 |
143 | {% highlight javascript %}
144 | // change MyTitle's inside h1
145 | h1({ style: {color: this.props.color} }, this.props.title)
146 |
147 | // change MyFirstComponent inside div
148 | MyTitleFact({title: 'Props are great!', color: 'rebeccapurple'}),
149 | MyTitleFact({title: 'Use props everywhere!', color: 'mediumaquamarine'}),
150 | MyTitleFact({title: 'Props are the best!', color: 'peru'})
151 | {% endhighlight %}
152 |
153 | 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!
154 |
155 |
--------------------------------------------------------------------------------
/_posts/2016-01-04-tooling.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Tooling – standard and npm"
3 | ---
4 |
5 | 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.
6 |
7 | ## standard
8 |
9 | Let's start with the easiest to start with right now: standard.
10 |
11 | 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][eslint-docs]. 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][standard-checks].
12 |
13 | 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][semistandard]. But give standard a shot. You may like it.
14 |
15 | That being said, we _will_ introducing new rules for our JSX since we want some rules added to enforce good "Reactful" code.
16 |
17 | 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][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.
18 |
19 | ## npm scripts
20 |
21 | 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:
22 |
23 | {% highlight json %}
24 | "scripts": {
25 | "test": "standard"
26 | },
27 | {% endhighlight %}
28 |
29 | 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.
30 |
31 | 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.
32 |
33 | [eslint-docs]: http://eslint.org/
34 | [standard-checks]: https://github.com/feross/standard/blob/master/RULES.md#javascript-standard-style
35 | [semistandard]: https://github.com/Flet/semistandard
36 | [standard-format]: https://github.com/maxogden/standard-format
--------------------------------------------------------------------------------
/_posts/2016-01-05-tooling-2.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Tooling - webpack and Babel"
3 | ---
4 |
5 | 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.
6 |
7 | ## webpack
8 |
9 | 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.
10 |
11 | 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.
12 |
13 | 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.
14 |
15 | 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">
to <​script src="public/bundle.js">
.
16 |
17 | 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.
18 |
19 | In ClientApp.js, remove the global comment at the top and add at the top
20 |
21 | {% highlight javascript %}
22 | var React = require('react')
23 | var ReactDOM = require('react-dom')
24 | {% endhighlight %}
25 |
26 | 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!
27 |
28 | 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!
29 |
30 | ## Babel
31 |
32 | 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.
33 |
34 | 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.
35 |
36 | Create a new file called .babelrc. Inside .babelrc, put:
37 |
38 | {% highlight json %}
39 | {
40 | "presets": ["react", "es2015"]
41 | }
42 | {% endhighlight %}
43 |
44 | 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.
45 |
46 | ## webpack loaders
47 |
48 | 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.
49 |
50 | {% highlight javascript %}
51 | const path = require('path')
52 |
53 | module.exports = {
54 | context: __dirname,
55 | entry: './js/ClientApp.js',
56 | output: {
57 | path: path.join(__dirname, '/public'),
58 | filename: 'bundle.js'
59 | },
60 | resolve: {
61 | extensions: ['', '.js', '.jsx', '.json']
62 | },
63 | stats: {
64 | colors: true,
65 | reasons: true,
66 | chunks: false
67 | },
68 | module: {
69 | loaders: [
70 | {
71 | test: /\.jsx?$/,
72 | loader: 'babel-loader'
73 | }
74 | ]
75 | }
76 | }
77 |
78 | {% endhighlight %}
79 |
80 | 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.
81 |
--------------------------------------------------------------------------------
/_posts/2016-01-06-jsx.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "JSX"
3 | ---
4 |
5 | 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.
6 |
7 | 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.
8 |
9 | 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.
10 |
11 | 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.
12 |
13 |
14 | 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.
15 |
16 | {% highlight javascript %}
17 | const React = require('react')
18 | const div = React.DOM.div
19 | const h1 = React.DOM.h1
20 |
21 | const MyTitle = React.createClass({
22 | render () {
23 | const style = {color: this.props.color}
24 | return (
25 | 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.
40 |
41 | 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.
42 |
43 | Let's rewrite ClientApp.js. Rename it to ClientApp.jsx.
44 |
45 | {% highlight javascript %}
46 | const React = require('react')
47 | const ReactDOM = require('react-dom')
48 | const MyTitle = require('./MyTitle')
49 |
50 | const MyFirstComponent = () => (
51 | 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.
36 |
37 | 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.
38 |
39 | {% highlight json %}
40 | // in package.json in scripts
41 | "watch": "webpack --watch",
42 | {% endhighlight %}
43 |
44 | 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.
45 |
46 | 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.
47 |
48 | Create a file called .eslintrc.json
. That first period is significant. Inside the new file put:
49 |
50 | {% highlight json %}
51 | {
52 | "extends": ["standard", "standard-react"]
53 | }
54 | {% endhighlight %}
55 |
56 | 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.
57 |
58 | {% highlight javascript %}
59 | // inside module, before loaders
60 | preLoaders: [
61 | {
62 | test: /\.jsx?$/,
63 | loader: "eslint-loader",
64 | exclude: /node_modules/
65 | }
66 | ],
67 | {% endhighlight %}
68 |
69 | 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.
70 |
71 | 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.
72 |
73 | [radium]: http://stack.formidable.com/radium/
--------------------------------------------------------------------------------
/_posts/2016-01-08-react-router.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "React Router"
3 | ---
4 |
5 | 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]. 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.
6 |
7 | 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.
8 |
9 | {% highlight javascript %}
10 | // Landing.jsx
11 | const React = require('react')
12 |
13 | const Landing = () => (
14 | const { Router, Route, hashHistory } = ReactRouter
code looks foreign to you, check out [2ality's post on it][destructuring].
44 |
45 | So now we got react-router rolling, let's move onto our second page, the search page.
46 |
47 | 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.)
48 |
49 | 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.
50 |
51 | {% highlight javascript %}
52 | {
53 | test: /\.json$/,
54 | loader: 'json-loader'
55 | }
56 | {% endhighlight %}
57 |
58 | Create a new file called Search.jsx. In Search.jsx put:
59 |
60 | {% highlight javascript %}
61 | const React = require('react')
62 | const data = require('../public/data')
63 |
64 | const Search = () => (
65 | or Browse All
to
92 |
93 | {% highlight javascript %}
94 | // add at top with other requires
95 | const ReactRouter = require('react-router')
96 | const { Link } = ReactRouter
97 |
98 | // change to
99 | or Browse All
100 | {% endhighlight %}
101 |
102 | 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.
103 |
104 | [react-router]: https://github.com/reactjs/react-router
105 | [destructuring]: http://www.2ality.com/2015/01/es6-destructuring.html#destructuring
--------------------------------------------------------------------------------
/_posts/2016-01-09-react-props.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "React: Props"
3 | ---
4 |
5 | Let's start making our search page. Replace JSX HTML in Search with
6 |
7 | {% highlight javascript %}
8 | {show.description}
46 |{props.show.description}
70 |
to
. This will take all the properties from show and spread them out as individual properties on ShowCard. You _could_ write
but that's a lot of writing and this cuts an easy corner. Let's go modify ShowCard to match.
125 |
126 | {% highlight javascript %}
127 | const React = require('react')
128 |
129 | const ShowCard = (props) => (
130 | {props.description}
136 |children
is in particular an interesting beast. It allows you to make a tag and have access to whatever's inside. So:
195 |
196 | {% highlight html %}
197 | 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.
110 |
111 | 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:
112 |
113 | {% highlight javascript %}
114 |
115 | {% endhighlight %}
116 |
117 | 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.
118 |
119 | {% highlight javascript %}
120 | class Search extends React.Component {
121 | constructor (props) {
122 | super(props)
123 |
124 | this.state = {
125 | searchTerm: 'this is the default searchTerm'
126 | }
127 |
128 | this.handleSearchTermChange = this.handleSearchTermChange.bind(this)
129 | }
130 | handleSearchTermChange (event) {
131 | this.setState({ searchTerm: event.target.value })
132 | }
133 | render () {
134 | return (
135 | 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.
152 |
153 | So go back now and change the brand to the correct title.
154 |
155 | 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.
156 |
157 | {% highlight javascript %}
158 | -R nyan
to the end for fun too. This changes the reporter to be the [Nyan Cat][nyan] and I just can't help my self. There are other [reporters][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
31 |
32 | 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.)
33 |
34 | In your new file, put:
35 |
36 | {% highlight javascript %}
37 | /* eslint-env mocha */
38 | const chai = require('chai')
39 | const { expect } = chai
40 |
41 | describe('console.log(wrapper.debug())
. That will show you what it's dealing with. Let's add another more useful test.
70 |
71 | {% highlight javascript %}
72 | // add two more requires at the top
73 | const ShowCard = require('../js/ShowCard')
74 | const data = require('../public/data')
75 |
76 | // add another test inside the describe
77 | it('should render as many shows as there are data for', () => {
78 | const wrapper = shallow(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.
14 |
15 | 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.
16 |
17 | [istanbul]: https://github.com/gotwarlost/istanbul
18 | [nyc]: https://github.com/bcoe/nyc
--------------------------------------------------------------------------------
/_posts/2016-01-13-hot-module-reload.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "A Note on Hot Module Reload"
3 | ---
4 |
5 | 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.
6 |
7 | 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.
8 |
9 | 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][hmr-death].
10 |
11 | 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.
12 |
13 | [hmr-death]: https://medium.com/@dan_abramov/the-death-of-react-hot-loader-765fa791d7c4#.80xafsv2d
--------------------------------------------------------------------------------
/_posts/2016-01-14-react-marshall-data.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Data in React"
3 | ---
4 |
5 | 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.
6 |
7 | Create a new file in js/ called Details.jsx. In Details put:
8 |
9 | {% highlight javascript %}
10 | const React = require('react')
11 |
12 | class Details extends React.Component {
13 | render () {
14 | return (
15 | [stuff]/index.html#/details/1
. You should see your new component here.
36 |
37 | Let's show you a neat debugging tip I totally stole from [Ryan Florence][ryflo]. replace that h1 with this:
38 |
39 | {% highlight javascript %}
40 | // instead of the h1 in render
41 |
42 | {JSON.stringify(this.props.params, null, 4)}
43 |
44 |
45 | // at the bottom to shut up lint
46 | Details.propTypes = {
47 | params: React.PropTypes.object
48 | }
49 | {% endhighlight %}
50 |
51 | 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
52 |
53 | 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.
54 |
55 | {% highlight javascript %}
56 | // another require
57 | const data = require('../public/data')
58 |
59 | // modify the existing route
60 | {description}
137 |{props.description}
265 |$r
. It should be a reference to the element you have selected in the virtual DOM and you can manipulate it.
19 | - 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.
20 | - iframes and Chrome extensions don't work with React Dev Tools as of writing.
--------------------------------------------------------------------------------
/_posts/2016-01-16-redux.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "redux"
3 | ---
4 |
5 | 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.
6 |
7 | 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][flux] and the [Elm][elm] architecture. If you're going to use a Flux-like architecture in your app, make it redux.
8 |
9 | So what is redux? [Redux is a predictable state container for JavaScript apps.][redux] 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.
10 |
11 | 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.
12 |
13 | 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.
14 |
15 | 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!
16 |
17 | {% highlight javascript %}
18 | const redux = require('redux')
19 | const reactRedux = require('react-redux')
20 |
21 | const SET_SEARCH_TERM = 'setSearchTerm'
22 |
23 | const reducer = (state = {searchTerm: ''}, action) => {
24 | switch (action.type) {
25 | case SET_SEARCH_TERM:
26 | const newState = {}
27 | Object.assign(newState, state, {searchTerm: action.value})
28 | return newState
29 | default:
30 | return state
31 | }
32 | }
33 |
34 | const store = redux.createStore(reducer)
35 |
36 | const mapStateToProps = (state) => ({ searchTerm: state.searchTerm })
37 | const mapDispatchToProps = (dispatch) => {
38 | return {
39 | setSearchTerm: (term) => {
40 | dispatch({type: SET_SEARCH_TERM, value: term})
41 | }
42 | }
43 | }
44 |
45 | const connector = reactRedux.connect(mapStateToProps, mapDispatchToProps)
46 |
47 | module.exports = { connector, store }
48 | {% endhighlight %}
49 |
50 | 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.
51 |
52 | Then to create a new store you just give redux the root reducer and it's created! Cool!
53 |
54 | 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.
55 |
56 | Let's do some connecting with ClientApp.jsx
57 |
58 | {% highlight javascript %}
59 | // more requires
60 | const Store = require('./Store')
61 | const { store } = Store
62 | const reactRedux = require('react-redux')
63 | const { Provider } = reactRedux
64 |
65 | // change the ReactDOM render call
66 | ReactDOM.render(
67 | (
68 | 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.
77 |
78 | Let's make it work with Header so it correctly displays and changes the redux store.
79 |
80 | {% highlight javascript %}
81 | // another require
82 | const Store = require('./Store')
83 | const { connector } = Store
84 |
85 | // wrap component
86 | module.exports = connector(Header)
87 |
88 | // two more methods for component
89 | constructor (props) {
90 | super(props)
91 |
92 | this.handleSearchTermChange = this.handleSearchTermChange.bind(this)
93 | }
94 | handleSearchTermChange (e) {
95 | this.props.setSearchTerm(e.target.value)
96 | }
97 |
98 | // change input
99 | utilSpace =
100 |
101 | // change propTypes
102 | Header.propTypes = {
103 | setSearchTerm: React.PropTypes.func,
104 | showSearch: React.PropTypes.bool,
105 | searchTerm: React.PropTypes.string
106 | }
107 | {% endhighlight %}
108 |
109 | 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.
110 |
111 | {% highlight javascript %}
112 | // more requires
113 | const Store = require('./Store')
114 | const { connector } = Store
115 |
116 | // delete constructor
117 |
118 | // delete handleSearchTermChange method
119 |
120 | // change Header
121 | file:///
protocol. So we're going to use node's [http-server][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.
23 |
24 | 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.
25 |
26 | [chrome-extension]: https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd?hl=en
27 | [http-server]: https://github.com/indexzero/http-server
--------------------------------------------------------------------------------
/_posts/2016-01-18-fixing-tests.markdown:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Fixing Our Tests and Testing redux"
3 | ---
4 |
5 | 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.
6 |
7 | 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.
8 |
9 | The first test is actually testing Header so let's move it to its own describe too.
10 |
11 | {% highlight javascript %}
12 | // change what's pulled in from enzyme
13 | const { render } = enzyme
14 |
15 | // more requires
16 | const Header = require('../js/Header')
17 | const Store = require('../js/Store')
18 | const { store } = Store
19 |
20 | // new describe
21 | describe('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:
82 |
83 | {% highlight javascript %}
84 | const React = require('react')
85 | const ReactDOM = require('react-dom')
86 | const App = require('./ClientApp')
87 |
88 | ReactDOM.render(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.
92 |
93 | Go back to ClientApp.jsx
94 |
95 | {% highlight javascript %}
96 | // delete hashHistory from ReactRouter destructuring, add browserHistory
97 | const { Router, Route, IndexRoute, browserHistory } = ReactRouter
98 |
99 | // delete data.json require
100 |
101 | // change attribute of 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.
10 |
11 | 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!
12 |
13 | So first we need give a minor tweak to webpack.config.js.
14 |
15 | {% highlight javascript %}
16 | // inside of the output config object
17 | publicPath: '/public/'
18 |
19 | // inside stats
20 | chunks: true
21 | {% endhighlight%}
22 |
23 | 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.
24 |
25 | Go to ClientApp
26 |
27 | {% highlight javascript %}
28 | // delete requires for Landing, Search, Details, Route, and IndexRoute
29 |
30 | // after the requires
31 | if (typeof module !== 'undefined' && module.require) {
32 | if (typeof require.ensure === 'undefined') {
33 | require.ensure = require('node-ensure')// shim for node.js
34 | }
35 | }
36 |
37 | // replace myRoutes
38 | const rootRoute = {
39 | component: Layout,
40 | path: '/',
41 | indexRoute: {
42 | getComponent (location, cb) {
43 | require.ensure([], (error) => {
44 | if (error) {
45 | return console.error('ClientApp Landing require.ensure error', error)
46 | }
47 | cb(null, require('./Landing'))
48 | })
49 | }
50 | },
51 | childRoutes: [
52 | {
53 | path: 'search',
54 | getComponent (location, cb) {
55 | require.ensure([], (error) => {
56 | if (error) {
57 | return console.error('ClientApp Search require.ensure error', error)
58 | }
59 | cb(null, require('./Search'))
60 | })
61 | }
62 | },
63 | {
64 | path: 'details/:id',
65 | getComponent (location, cb) {
66 | require.ensure([], (error) => {
67 | if (error) {
68 | return console.error('ClientApp Details require.ensure error', error)
69 | }
70 | cb(null, require('./Details'))
71 | })
72 | }
73 | }
74 | ]
75 | }
76 |
77 | // replace Router and its children
78 |