├── .gitignore ├── README.md └── lessons ├── 01-setting-up ├── README.md ├── index.html ├── index.js ├── modules │ └── App.js ├── package.json └── webpack.config.js ├── 02-rendering-a-route ├── README.md ├── index.html ├── index.js ├── modules │ └── App.js ├── package.json └── webpack.config.js ├── 03-navigating-with-link ├── README.md ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 04-nested-routes ├── README.md ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 05-active-links ├── README.md ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 06-params ├── README.md ├── index.css ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── NavLink.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 07-more-nesting ├── README.md ├── index.css ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 08-index-routes ├── README.md ├── index.css ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 09-index-links ├── README.md ├── index.css ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── Home.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 10-clean-urls ├── README.md ├── index.css ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── Home.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 11-productionish-server ├── README.md ├── index.css ├── index.html ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── Home.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json └── webpack.config.js ├── 12-navigating ├── README.md ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── Home.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json ├── public │ ├── index.css │ └── index.html ├── server.js └── webpack.config.js ├── 13-server-rendering ├── README.md ├── index.js ├── modules │ ├── About.js │ ├── App.js │ ├── Home.js │ ├── NavLink.js │ ├── Repo.js │ └── Repos.js ├── package.json ├── public │ ├── index.css │ └── index.html ├── server.js └── webpack.config.js └── 14-whats-next ├── README.md ├── index.js ├── modules ├── About.js ├── App.js ├── Home.js ├── NavLink.js ├── Repo.js ├── Repos.js └── routes.js ├── package.json ├── public └── index.css ├── server.js ├── webpack.config.js └── webpack.server.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | bundle.js 2 | *.bundle.js 3 | node_modules 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | React Router Tutorial 2 | ===================== 3 | 4 | Quick lessons for getting up-to-speed with React Router. 5 | 6 | See [Lesson 1 - Setting Up](/lessons/01-setting-up/) to get started. 7 | 8 | Each lesson is a fully runnable app with all code from the previous lesson, so you can `cd lessons/`, npm install, 9 | and then run the appropriate NPM scripts for each lesson from within the lesson folder. 10 | 11 | Missing stuff that will come eventually, hopefully ... maybe. 12 | 13 | 1. an app that isn't completely pointless 14 | - egghead.io videos 15 | - code splitting 16 | - location state 17 | - data integration 18 | -------------------------------------------------------------------------------- /lessons/01-setting-up/README.md: -------------------------------------------------------------------------------- 1 | # Setting up the Project 2 | 3 | First you'll need [Node.js](https://nodejs.org) and the package manager 4 | that comes with it: [npm](https://www.npmjs.com/). 5 | 6 | Once you've got that working, head to the command line where we'll set 7 | up our project. 8 | 9 | ## Clone the Tutorial 10 | 11 | ``` 12 | git clone https://github.com/reactjs/react-router-tutorial 13 | cd react-router-tutorial 14 | cd lessons/01-setting-up 15 | npm install 16 | npm start 17 | ``` 18 | 19 | Now open up [http://localhost:8080](http://localhost:8080) 20 | 21 | Feel free to poke around the code to see how we're using webpack and npm 22 | scripts to run the app. 23 | 24 | You should see a "Hello React Router" message in the browser. 25 | 26 | ## Make Some Changes 27 | 28 | Open up `modules/App.js` and change the text to something like "Hello 29 | ". The browser automatically reloads with your new code. 30 | 31 | --- 32 | 33 | [Next: Rendering a Router](../02-rendering-a-route/) 34 | -------------------------------------------------------------------------------- /lessons/01-setting-up/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 |
6 | 7 | -------------------------------------------------------------------------------- /lessons/01-setting-up/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import App from './modules/App' 4 | render(, document.getElementById('app')) 5 | -------------------------------------------------------------------------------- /lessons/01-setting-up/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
Hello, React Router!
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/01-setting-up/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/01-setting-up/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/02-rendering-a-route/README.md: -------------------------------------------------------------------------------- 1 | # Rendering a Route 2 | 3 | At its heart, React Router is a component. 4 | 5 | ```js 6 | render(, document.getElementById('app')) 7 | ``` 8 | 9 | That's not going to display anything until we configure a route. 10 | 11 | Open up `index.js` and 12 | 13 | 1. import `Router`, `Route`, and `hashHistory` 14 | 2. render a `Router` instead of `App` 15 | 16 | ```js 17 | // ... 18 | import { Router, Route, hashHistory } from 'react-router' 19 | 20 | render(( 21 | 22 | 23 | 24 | ), document.getElementById('app')) 25 | ``` 26 | 27 | Make sure your server is running with `npm start` and then visit 28 | [http://localhost:8080](http://localhost:8080) 29 | 30 | You should get the same screen as before, but this time with some junk 31 | in the URL. We're using `hashHistory`--it manages the routing history 32 | with the hash portion of the url. It's got that extra junk to shim some 33 | behavior the browser has natively when using real urls. We'll change 34 | this to use real urls later and lose the junk, but for now, this works 35 | great because it doesn't require any server-side configuration. 36 | 37 | ## Adding More Screens 38 | 39 | Create two new components at: 40 | 41 | - `modules/About.js` 42 | - `modules/Repos.js` 43 | 44 | ```js 45 | // modules/About.js 46 | import React from 'react' 47 | 48 | export default React.createClass({ 49 | render() { 50 | return
About
51 | } 52 | }) 53 | ``` 54 | 55 | ```js 56 | // modules/Repos.js 57 | import React from 'react' 58 | 59 | export default React.createClass({ 60 | render() { 61 | return
Repos
62 | } 63 | }) 64 | ``` 65 | 66 | Now we can couple them to the app at their respective paths. 67 | 68 | ```js 69 | // insert into index.js 70 | import About from './modules/About' 71 | import Repos from './modules/Repos' 72 | 73 | render(( 74 | 75 | 76 | {/* add the routes here */} 77 | 78 | 79 | 80 | ), document.getElementById('app')) 81 | ``` 82 | 83 | Now visit [http://localhost:8080/#/about](http://localhost:8080/#/about) and 84 | [http://localhost:8080/#/repos](http://localhost:8080/#/repos) 85 | 86 | --- 87 | 88 | [Next: Navigating With Link](../03-navigating-with-link/) 89 | -------------------------------------------------------------------------------- /lessons/02-rendering-a-route/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 |
6 | 7 | -------------------------------------------------------------------------------- /lessons/02-rendering-a-route/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import App from './modules/App' 4 | render(, document.getElementById('app')) 5 | -------------------------------------------------------------------------------- /lessons/02-rendering-a-route/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
Hello, React Router!
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/02-rendering-a-route/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/02-rendering-a-route/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/README.md: -------------------------------------------------------------------------------- 1 | # Navigating with Link 2 | 3 | Perhaps the most used component in your app is `Link`. It's almost 4 | identical to the `` tag you're used to except that it's aware of 5 | the `Router` it was rendered in. 6 | 7 | Let's create some navigation in our `App` component. 8 | 9 | ```js 10 | // modules/App.js 11 | import React from 'react' 12 | import { Link } from 'react-router' 13 | 14 | export default React.createClass({ 15 | render() { 16 | return ( 17 |
18 |

React Router Tutorial

19 |
    20 |
  • About
  • 21 |
  • Repos
  • 22 |
23 |
24 | ) 25 | } 26 | }) 27 | ``` 28 | 29 | Now visit [http://localhost:8080](http://localhost:8080) and click the links, click back, click 30 | forward. It works! 31 | 32 | --- 33 | 34 | [Next: Nested Routes](../04-nested-routes/) 35 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 |
6 | 7 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | 8 | render(( 9 | 10 | 11 | 12 | 13 | 14 | ), document.getElementById('app')) 15 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
About
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
Hello, React Router!
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
Repos
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/03-navigating-with-link/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/README.md: -------------------------------------------------------------------------------- 1 | # Nested Routes 2 | 3 | The navigation we added to `App` should probably be present on every 4 | screen. Without React Router, we could wrap that `ul` into a 5 | component, say `Nav`, and render a `Nav` on every one of our screens. 6 | 7 | This approach isn't as clean as the application grows. React Router 8 | provides another way to share UI like this with nested routes, a trick 9 | it learned from [Ember](http://emberjs.com) (/me tips hat). 10 | 11 | ## Nested UI and Nested URLs 12 | 13 | Have you ever noticed your app is just a series of boxes inside boxes 14 | inside boxes? Have you also noticed your URLs tend to be coupled to that 15 | nesting? For example given this url, `/repos/123`, our 16 | components would probably look like this: 17 | 18 | ```js 19 | {/* / */} 20 | {/* /repos */} 21 | {/* /repos/123 */} 22 | 23 | 24 | ``` 25 | 26 | And our UI something like: 27 | 28 | ``` 29 | +-------------------------------------+ 30 | | Home Repos About | <- App 31 | +------+------------------------------+ 32 | | | | 33 | Repos -> | repo | Repo 1 | 34 | | | | 35 | | repo | Boxes inside boxes | 36 | | | inside boxes ... | <- Repo 37 | | repo | | 38 | | | | 39 | | repo | | 40 | | | | 41 | +------+------------------------------+ 42 | ``` 43 | 44 | React Router embraces this by letting you nest your routes, which 45 | automatically becomes nested UI. 46 | 47 | ## Sharing Our Navigation 48 | 49 | Let's nest our `About` and `Repos` components inside of `App` so that we 50 | can share the navigation with all screens in the app. We do it in two 51 | steps: 52 | 53 | First, let the `App` `Route` have children, and move the other routes 54 | underneath it. 55 | 56 | ```js 57 | // index.js 58 | // ... 59 | render(( 60 | 61 | 62 | {/* make them children of `App` */} 63 | 64 | 65 | 66 | 67 | ), document.getElementById('app')) 68 | ``` 69 | 70 | Next, render children inside of `App`. 71 | 72 | ```js 73 | // modules/App.js 74 | // ... 75 | render() { 76 | return ( 77 |
78 |

React Router Tutorial

79 |
    80 |
  • About
  • 81 |
  • Repos
  • 82 |
83 | 84 | {/* add this */} 85 | {this.props.children} 86 | 87 |
88 | ) 89 | } 90 | // ... 91 | ``` 92 | 93 | Alright, now go click the links and notice that the `App` component 94 | continues to render while the child route's component gets swapped 95 | around as `this.props.children` :) 96 | 97 | React Router is constructing your UI like this: 98 | 99 | ```js 100 | // at /about 101 | 102 | 103 | 104 | 105 | // at /repos 106 | 107 | 108 | 109 | ``` 110 | 111 | ## By Small and Simple Things are Great Things Brought to Pass 112 | 113 | The best way to build large things is to stitch small things together. 114 | 115 | This is the real power of React Router, every route can be developed 116 | (even rendered!) as an independent application. Your route configuration 117 | stitches all these apps together however you'd like. Applications 118 | inside of Applications, boxes inside of boxes. 119 | 120 | What happens if you move the `About` route outside of `App`? 121 | 122 | Okay, now put it back. 123 | 124 | --- 125 | 126 | [Next: Active Links](../05-active-links/) 127 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 |
6 | 7 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | 8 | render(( 9 | 10 | 11 | 12 | 13 | 14 | ), document.getElementById('app')) 15 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
About
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
8 |

React Router Tutorial

9 |
    10 |
  • About
  • 11 |
  • Repos
  • 12 |
13 |
14 | ) 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
Repos
6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/04-nested-routes/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/05-active-links/README.md: -------------------------------------------------------------------------------- 1 | # Active Links 2 | 3 | One way that `Link` is different from `a` is that it knows if the path 4 | it links to is active so you can style it differently. 5 | 6 | ## Active Styles 7 | 8 | Let's see how it looks with inline styles, add `activeStyle` to your 9 | `Link`s. 10 | 11 | ```js 12 | // modules/App.js 13 |
  • About
  • 14 |
  • Repos
  • 15 | ``` 16 | 17 | Now as you navigate, the active link is red. 18 | 19 | ## Active Class Name 20 | 21 | You can also use an active class name instead of inline-styles. 22 | 23 | ```js 24 | // modules/App.js 25 |
  • About
  • 26 |
  • Repos
  • 27 | ``` 28 | 29 | We don't have a stylesheet on the page yet though. Lets add one-extra 30 | point if you can add a `link` tag from memory. 31 | 32 | ```html 33 | // index.html 34 | 35 | ``` 36 | 37 | And the CSS file: 38 | 39 | ```css 40 | .active { 41 | color: green; 42 | } 43 | ``` 44 | 45 | You'll need to manually refresh the browser since Webpack isn't building 46 | our `index.html`. 47 | 48 | ## Nav Link Wrappers 49 | 50 | Most links in your site don't need to know they are active, usually just 51 | primary navigation links need to know. It's useful to wrap those so you 52 | don't have to remember what your `activeClassName` or `activeStyle` is 53 | everywhere. 54 | 55 | We will use a spread operator here, the three dots. It clones our props 56 | and in this use case it clones `activeClassName` to our desired component for 57 | us to benefit from. 58 | 59 | Create a new file at `modules/NavLink.js` that looks like this: 60 | 61 | ```js 62 | // modules/NavLink.js 63 | import React from 'react' 64 | import { Link } from 'react-router' 65 | 66 | export default React.createClass({ 67 | render() { 68 | return 69 | } 70 | }) 71 | ``` 72 | 73 | Now you can go change your links to `NavLink`s. 74 | 75 | ```js 76 | // modules/App.js 77 | import NavLink from './NavLink' 78 | 79 | // ... 80 | 81 |
  • About
  • 82 |
  • Repos
  • 83 | ``` 84 | 85 | Oh, how beautiful upon the renders is the composability of components. 86 | 87 | --- 88 | 89 | [Next: Params](../06-params/) 90 | -------------------------------------------------------------------------------- /lessons/05-active-links/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 |
    6 | 7 | -------------------------------------------------------------------------------- /lessons/05-active-links/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | 8 | render(( 9 | 10 | 11 | 12 | 13 | 14 | 15 | ), document.getElementById('app')) 16 | -------------------------------------------------------------------------------- /lessons/05-active-links/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/05-active-links/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • About
    • 11 |
    • Repos
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/05-active-links/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Repos
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/05-active-links/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/05-active-links/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/06-params/README.md: -------------------------------------------------------------------------------- 1 | # URL Params 2 | 3 | Consider the following URLs: 4 | 5 | ``` 6 | /repos/reactjs/react-router 7 | /repos/facebook/react 8 | ``` 9 | 10 | These URLs would match a route path like this: 11 | 12 | ``` 13 | /repos/:userName/:repoName 14 | ``` 15 | 16 | The parts that start with `:` are URL parameters whose values will be 17 | parsed out and made available to route components on 18 | `this.props.params[name]`. 19 | 20 | ## Adding a Route with Parameters 21 | 22 | Let's teach our app how to render screens at `/repos/:userName/:repoName`. 23 | 24 | First we need a component to render at the route, make a new file at 25 | `modules/Repo.js` that looks something like this: 26 | 27 | ```js 28 | // modules/Repo.js 29 | import React from 'react' 30 | 31 | export default React.createClass({ 32 | render() { 33 | return ( 34 |
    35 |

    {this.props.params.repoName}

    36 |
    37 | ) 38 | } 39 | }) 40 | ``` 41 | 42 | Now open up `index.js` and add the new route. 43 | 44 | ```js 45 | // ... 46 | // import Repo 47 | import Repo from './modules/Repo' 48 | 49 | render(( 50 | 51 | 52 | 53 | {/* add the new route */} 54 | 55 | 56 | 57 | 58 | ), document.getElementById('app')) 59 | ``` 60 | 61 | Now we can add some links to this new route in `Repos.js`. 62 | 63 | ```js 64 | // Repos.js 65 | import { Link } from 'react-router' 66 | // ... 67 | export default React.createClass({ 68 | render() { 69 | return ( 70 |
    71 |

    Repos

    72 | 73 | {/* add some links */} 74 |
      75 |
    • React Router
    • 76 |
    • React
    • 77 |
    78 | 79 |
    80 | ) 81 | } 82 | }) 83 | ``` 84 | 85 | Now go test your links out. Note that the parameter name in the route 86 | `path` becomes the property name in the component. Both `repoName` and 87 | `userName` are available on `this.props.params` of your component. You 88 | should probably add some prop types to help others and yourself out 89 | later. 90 | 91 | --- 92 | 93 | [Next: More Nesting](../07-more-nesting/) 94 | -------------------------------------------------------------------------------- /lessons/06-params/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/06-params/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/06-params/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | 8 | render(( 9 | 10 | 11 | 12 | 13 | 14 | 15 | ), document.getElementById('app')) 16 | -------------------------------------------------------------------------------- /lessons/06-params/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/06-params/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • About
    • 11 |
    • Repos
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/06-params/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/06-params/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Repos
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/06-params/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/06-params/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/README.md: -------------------------------------------------------------------------------- 1 | # More Nesting 2 | 3 | Notice how the list of links to different repositories goes away when we 4 | navigate to a repository? What if we want the list to persist, just like 5 | the global navigation persists? 6 | 7 | Try to figure that out before reading on. 8 | 9 | ... 10 | 11 | First, nest the `Repo` route under the `Repos` route. Then go render 12 | `this.props.children` in `Repos`. 13 | 14 | ```js 15 | // index.js 16 | // ... 17 | 18 | 19 | 20 | ``` 21 | 22 | ```js 23 | // Repos.js 24 | // ... 25 |
    26 |

    Repos

    27 |
      28 |
    • React Router
    • 29 |
    • React
    • 30 |
    31 | {/* will render `Repo.js` when at /repos/:userName/:repoName */} 32 | {this.props.children} 33 |
    34 | ``` 35 | 36 | ## Active Links 37 | 38 | Let's bring in our `NavLink` from before so we can add the `active` 39 | class name to these links: 40 | 41 | ```js 42 | // modules/Repos.js 43 | // import it 44 | import NavLink from './NavLink' 45 | 46 | // ... 47 |
  • React Router
  • 48 |
  • React
  • 49 | // ... 50 | ``` 51 | 52 | Notice how both the `/repos` link up top and the individual repo links are 53 | both active? When child routes are active, so are the parents. 54 | 55 | --- 56 | 57 | [Next: Index Routes](../08-index-routes/) 58 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | 9 | render(( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ), document.getElementById('app')) 18 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • About
    • 11 |
    • Repos
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.params.repoName}

    8 |
    9 | ) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'react-router' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    Repos

    9 |
      10 |
    • React Router
    • 11 |
    • React
    • 12 |
    13 |
    14 | ) 15 | } 16 | }) 17 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/07-more-nesting/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/08-index-routes/README.md: -------------------------------------------------------------------------------- 1 | # Index Routes 2 | 3 | When we visit `/` in this app it's just our navigation and a blank page. 4 | We'd like to render a `Home` component there. Lets create a `Home` 5 | component and then talk about how to render it at `/`. 6 | 7 | ```js 8 | // modules/Home.js 9 | import React from 'react' 10 | 11 | export default React.createClass({ 12 | render() { 13 | return
    Home
    14 | } 15 | }) 16 | ``` 17 | 18 | One option is to see if we have any children in `App`, and if not, 19 | render `Home`: 20 | 21 | ```js 22 | // modules/App.js 23 | import Home from './Home' 24 | 25 | // ... 26 |
    27 | {/* ... */} 28 | {this.props.children || } 29 |
    30 | //... 31 | ``` 32 | 33 | This would work fine, but its likely we'll want `Home` to be attached to 34 | a route like `About` and `Repos` in the future. A few reasons include: 35 | 36 | 1. Participating in a data fetching abstraction that relies on matched 37 | routes and their components. 38 | 2. Participating in `onEnter` hooks 39 | 3. Participating in code-splitting 40 | 41 | Also, it just feels good to keep `App` decoupled from `Home` and let the 42 | route config decide what to render as the children. Remember, we want to 43 | build small apps inside small apps, not big ones! 44 | 45 | Let's add a new route to `index.js`. 46 | 47 | ```js 48 | // index.js 49 | // new imports: 50 | // add `IndexRoute` to 'react-router' imports 51 | import { Router, Route, hashHistory, IndexRoute } from 'react-router' 52 | // and the Home component 53 | import Home from './modules/Home' 54 | 55 | // ... 56 | 57 | render(( 58 | 59 | 60 | 61 | {/* add it here, as a child of `/` */} 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | ), document.getElementById('app')) 71 | ``` 72 | 73 | Now open [http://localhost:8080](http://localhost:8080) and you'll see the new component is 74 | rendered. 75 | 76 | Notice how the `IndexRoute` has no path. It becomes 77 | `this.props.children` of the parent when no other child of the parent 78 | matches, or in other words, when the parent's route matches exactly. 79 | 80 | Index routes can twist people's brains up sometimes. Hopefully it will 81 | sink in with a bit more time. Just think about a web server that looks 82 | for `index.html` when you're at `/`. Same idea, React Router looks for 83 | an index route if a route's path matches exactly. 84 | 85 | --- 86 | 87 | [Next: Index Links](../09-index-links/) 88 | -------------------------------------------------------------------------------- /lessons/08-index-routes/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/08-index-routes/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/08-index-routes/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | 9 | render(( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | ), document.getElementById('app')) 19 | -------------------------------------------------------------------------------- /lessons/08-index-routes/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/08-index-routes/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • About
    • 11 |
    • Repos
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/08-index-routes/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/08-index-routes/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.params.repoName}

    8 |
    9 | ) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/08-index-routes/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    Repos

    9 |
      10 |
    • React Router
    • 11 |
    • React
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/08-index-routes/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/08-index-routes/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/09-index-links/README.md: -------------------------------------------------------------------------------- 1 | # Index Links 2 | 3 | Have you noticed in our app that we don't have any navigation to get 4 | back to rendering the `Home` component? 5 | 6 | Lets add a link to `/` and see what happens: 7 | 8 | ```js 9 | // in App.js 10 | // ... 11 |
  • Home
  • 12 | // ... 13 | ``` 14 | 15 | Now navigate around. Notice anything weird? The link to `Home` is always 16 | active! As we learned earlier, parent routes are active when child routes 17 | are active. Unfortunately, `/` is the parent of everything. 18 | 19 | For this link, we want it to only be active when the index route is 20 | active. There are two ways to let the router know you're linking to the 21 | "index route" so it only adds the active class (or styles) when the 22 | index route is rendered. 23 | 24 | ## IndexLink 25 | 26 | First let's use the `IndexLink` instead of `NavLink` 27 | 28 | ```js 29 | // App.js 30 | import { IndexLink } from 'react-router' 31 | 32 | // ... 33 |
  • Home
  • 34 | ``` 35 | 36 | Fixed! Now this link is only "active" when we're at the index route. Go 37 | ahead and click around to see. 38 | 39 | ## `onlyActiveOnIndex` Property 40 | 41 | We can use `Link` as well by passing it the `onlyActiveOnIndex` prop 42 | (`IndexLink` just wraps `Link` with this property for convenience). 43 | 44 | ```js 45 |
  • Home
  • 46 | ``` 47 | 48 | That's fine, but we already abstracted away having to know what the 49 | `activeClassName` is with `Nav`. 50 | 51 | Remember, in `NavLink` we're passing along all of our props to `Link` with 52 | the `{...spread}` syntax, so we can actually add the prop when we render 53 | a `NavLink` and it will make its way down to the `Link`: 54 | 55 | ```js 56 |
  • Home
  • 57 | ``` 58 | 59 | --- 60 | 61 | [Next: Clean URLs](../10-clean-urls/) 62 | -------------------------------------------------------------------------------- /lessons/09-index-links/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/09-index-links/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/09-index-links/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory, IndexRoute } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | import Home from './modules/Home' 9 | 10 | render(( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ), document.getElementById('app')) 21 | -------------------------------------------------------------------------------- /lessons/09-index-links/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/09-index-links/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • About
    • 11 |
    • Repos
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/09-index-links/modules/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Home
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/09-index-links/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/09-index-links/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.params.repoName}

    8 |
    9 | ) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/09-index-links/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    Repos

    9 |
      10 |
    • React Router
    • 11 |
    • React
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/09-index-links/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/09-index-links/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/README.md: -------------------------------------------------------------------------------- 1 | # Clean URLs with Browser History 2 | 3 | The URLs in our app right now are built on a hack: the hash. It's the 4 | default because it will always work, but there's a better way. 5 | 6 | Modern browsers let JavaScript manipulate the URL without making an http 7 | request, so we don't need to rely on the hash (`#`) portion of the url 8 | to do routing, but there's a catch (we'll get to it later). 9 | 10 | ## Configuring Browser History 11 | 12 | Open up `index.js` and import `browserHistory` instead of `hashHistory`. 13 | 14 | ```js 15 | // index.js 16 | // ... 17 | // bring in `browserHistory` instead of `hashHistory` 18 | import { Router, Route, browserHistory, IndexRoute } from 'react-router' 19 | 20 | render(( 21 | 22 | {/* ... */} 23 | 24 | ), document.getElementById('app')) 25 | ``` 26 | 27 | Now go click around and admire your clean URLs. 28 | 29 | Oh yeah, the catch. Click on a link and then refresh your browser. What 30 | happens? 31 | 32 | ``` 33 | Cannot GET /repos 34 | ``` 35 | 36 | ## Configuring Your Server 37 | 38 | Your server needs to deliver your app no matter what URL comes in, 39 | because your app, in the browser, is manipulating the URL. Our current 40 | server doesn't know how to handle the URL. 41 | 42 | The Webpack Dev Server has an option to enable this. Open up 43 | `package.json` and add `--history-api-fallback`. 44 | 45 | ```json 46 | "start": "webpack-dev-server --inline --content-base . --history-api-fallback" 47 | ``` 48 | 49 | We also need to change our relative paths to absolute paths in 50 | `index.html` since the URLs will be at deep paths and the app, if it 51 | starts at a deep path, won't be able to find the files. 52 | 53 | ```html 54 | 55 | 56 | 57 | 58 | 59 | 60 | ``` 61 | 62 | Stop your server if it's running, then `npm start` again. Look at those 63 | clean URLs :) 64 | 65 | --- 66 | 67 | [Next: Production-ish Server](../11-productionish-server/) 68 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, hashHistory, IndexRoute } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | import Home from './modules/Home' 9 | 10 | render(( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ), document.getElementById('app')) 21 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • Home
    • 11 |
    • About
    • 12 |
    • Repos
    • 13 |
    14 | {this.props.children} 15 |
    16 | ) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/modules/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Home
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.params.repoName}

    8 |
    9 | ) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    Repos

    9 |
      10 |
    • React Router
    • 11 |
    • React
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base ." 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/10-clean-urls/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/README.md: -------------------------------------------------------------------------------- 1 | # Production-ish Server 2 | 3 | None of this has anything to do with React Router, but since we're 4 | talking about web servers, we might as well take it one step closer to 5 | the real-world. We'll also need it for server rendering in the next 6 | section. 7 | 8 | Webpack dev server is not a production server. Let's make a production 9 | server and a little environment-aware script to boot up the right server 10 | depending on the environment. 11 | 12 | Let's install a couple modules: 13 | 14 | ``` 15 | npm install express if-env compression --save 16 | ``` 17 | 18 | First, we'll use the handy `if-env` in `package.json`. Update your 19 | scripts entry in package.json to look like this: 20 | 21 | ```json 22 | // package.json 23 | "scripts": { 24 | "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev", 25 | "start:dev": "webpack-dev-server --inline --content-base . --history-api-fallback", 26 | "start:prod": "webpack && node server.js" 27 | }, 28 | ``` 29 | 30 | In the root directly, go open up `webpack.config.js` and add the publicPath '/' as per below: 31 | ``` 32 | // webpack.config.js 33 | output: { 34 | path: 'public', 35 | filename: 'bundle.js', 36 | publicPath: '/' 37 | }, 38 | ``` 39 | 40 | When you run `npm start` it checks if the value of our `NODE_ENV` environment variable is 41 | `production`. If yes, it runs `npm run start:prod`, if not, it runs 42 | `npm run start:dev`. 43 | 44 | Now we're ready to create a production server with Express and add a new file at root dir. Here's a 45 | first attempt: 46 | 47 | ```js 48 | // server.js 49 | var express = require('express') 50 | var path = require('path') 51 | 52 | var app = express() 53 | 54 | // serve our static stuff like index.css 55 | app.use(express.static(__dirname)) 56 | 57 | // send all requests to index.html so browserHistory in React Router works 58 | app.get('*', function (req, res) { 59 | res.sendFile(path.join(__dirname, 'index.html')) 60 | }) 61 | 62 | var PORT = process.env.PORT || 8080 63 | app.listen(PORT, function() { 64 | console.log('Production Express server running at localhost:' + PORT) 65 | }) 66 | ``` 67 | 68 | Now run: 69 | 70 | ```sh 71 | NODE_ENV=production npm start 72 | # For Windows users: 73 | # SET "NODE_ENV=production" && npm start 74 | ``` 75 | 76 | Congratulations! You now have a production server for this app. After 77 | clicking around, try navigating to [http://localhost:8080/package.json](http://localhost:8080/package.json). 78 | Whoops. Let's fix that. We're going to shuffle around a couple files and 79 | update some paths scattered across the app. 80 | 81 | 1. make a `public` directory. 82 | 2. Move `index.html` and `index.css` into it. 83 | 84 | Now let's update `server.js` to point to the right directory for static 85 | assets: 86 | 87 | ```js 88 | // server.js 89 | // ... 90 | // add path.join here 91 | app.use(express.static(path.join(__dirname, 'public'))) 92 | 93 | // ... 94 | app.get('*', function (req, res) { 95 | // and drop 'public' in the middle of here 96 | res.sendFile(path.join(__dirname, 'public', 'index.html')) 97 | }) 98 | ``` 99 | 100 | We also need to tell webpack to build to this new directory: 101 | 102 | ```js 103 | // webpack.config.js 104 | // ... 105 | output: { 106 | path: 'public', 107 | // ... 108 | } 109 | ``` 110 | 111 | And finally (!) add it to the `--content-base` argument to `npm run start:dev` script: 112 | 113 | ```json 114 | "start:dev": "webpack-dev-server --inline --content-base public --history-api-fallback", 115 | ``` 116 | 117 | If we had the time in this tutorial, we could use the `WebpackDevServer` 118 | API in a JavaScript file instead of the CLI in an npm script and then 119 | turn this path into config shared across all of these files. But, we're 120 | already on a tangent, so that will have to wait for another time. 121 | 122 | Okay, now that we aren't serving up the root of our project as public 123 | files, let's add some code minification to Webpack and gzipping to 124 | express. 125 | 126 | ```js 127 | // webpack.config.js 128 | 129 | // make sure to import this 130 | var webpack = require('webpack') 131 | 132 | module.exports = { 133 | // ... 134 | 135 | // add this handful of plugins that optimize the build 136 | // when we're in production 137 | plugins: process.env.NODE_ENV === 'production' ? [ 138 | new webpack.optimize.DedupePlugin(), 139 | new webpack.optimize.OccurrenceOrderPlugin(), 140 | new webpack.optimize.UglifyJsPlugin() 141 | ] : [], 142 | 143 | // ... 144 | } 145 | ``` 146 | 147 | And compression in express: 148 | 149 | ```js 150 | // server.js 151 | // ... 152 | var compression = require('compression') 153 | 154 | var app = express() 155 | // must be first! 156 | app.use(compression()) 157 | ``` 158 | 159 | Now go start your server in production mode: 160 | 161 | ``` 162 | NODE_ENV=production npm start 163 | ``` 164 | 165 | You'll see some UglifyJS logging and then in the browser, you can see 166 | the assets are being served with gzip compression. 167 | 168 | --- 169 | 170 | [Next: Navigating](../12-navigating/) 171 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, browserHistory, IndexRoute } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | import Home from './modules/Home' 9 | 10 | render(( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ), document.getElementById('app')) 21 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • Home
    • 11 |
    • About
    • 12 |
    • Repos
    • 13 |
    14 | {this.props.children} 15 |
    16 | ) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/modules/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Home
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.params.repoName}

    8 |
    9 | ) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    Repos

    9 |
      10 |
    • React Router
    • 11 |
    • React
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "webpack-dev-server --inline --content-base . --history-api-fallback" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "react": "^0.14.7", 13 | "react-dom": "^0.14.7", 14 | "react-router": "^2.0.0" 15 | }, 16 | "devDependencies": { 17 | "babel-core": "^6.5.1", 18 | "babel-loader": "^6.2.2", 19 | "babel-preset-es2015": "^6.5.0", 20 | "babel-preset-react": "^6.5.0", 21 | "http-server": "^0.8.5", 22 | "webpack": "^1.12.13", 23 | "webpack-dev-server": "^1.14.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lessons/11-productionish-server/webpack.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | entry: './index.js', 3 | 4 | output: { 5 | filename: 'bundle.js', 6 | publicPath: '/' 7 | }, 8 | 9 | module: { 10 | loaders: [ 11 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 12 | ] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lessons/12-navigating/README.md: -------------------------------------------------------------------------------- 1 | # Navigating Programatically 2 | 3 | While most navigation happens with `Link`, you can programmatically 4 | navigate around an application in response to form submissions, button 5 | clicks, etc. 6 | 7 | Let's make a little form in `Repos` that programmatically navigates. 8 | 9 | ```js 10 | // modules/Repos.js 11 | import React from 'react' 12 | import NavLink from './NavLink' 13 | 14 | export default React.createClass({ 15 | 16 | // add this method 17 | handleSubmit(event) { 18 | event.preventDefault() 19 | const userName = event.target.elements[0].value 20 | const repo = event.target.elements[1].value 21 | const path = `/repos/${userName}/${repo}` 22 | console.log(path) 23 | }, 24 | 25 | render() { 26 | return ( 27 |
    28 |

    Repos

    29 |
      30 |
    • React Router
    • 31 |
    • React
    • 32 | {/* add this form */} 33 |
    • 34 |
      35 | / {' '} 36 | {' '} 37 | 38 |
      39 |
    • 40 |
    41 | {this.props.children} 42 |
    43 | ) 44 | } 45 | }) 46 | ``` 47 | 48 | There are two ways you can do this, the first is simpler than the 49 | second. 50 | 51 | First we can use the `browserHistory` singleton that we passed into 52 | `Router` in `index.js` and push a new URL into the history. 53 | 54 | ```js 55 | // modules/Repos.js 56 | import { browserHistory } from 'react-router' 57 | 58 | // ... 59 | handleSubmit(event) { 60 | // ... 61 | const path = `/repos/${userName}/${repo}` 62 | browserHistory.push(path) 63 | }, 64 | // ... 65 | ``` 66 | 67 | There's a potential problem with this though. If you pass a different 68 | history to `Router` than you use here, it won't work. It's not very 69 | common to use anything other than `browserHistory`, so this is 70 | acceptable practice. If you're concerned about it, you can make a module 71 | that exports the history you want to use across the app, or... 72 | 73 | You can also use the `router` that `Router` provides on "context". 74 | First, you ask for context in the component, and then you can use it: 75 | 76 | ```js 77 | export default React.createClass({ 78 | 79 | // ask for `router` from context 80 | contextTypes: { 81 | router: React.PropTypes.object 82 | }, 83 | 84 | // ... 85 | 86 | handleSubmit(event) { 87 | // ... 88 | this.context.router.push(path) 89 | }, 90 | 91 | // .. 92 | }) 93 | ``` 94 | 95 | This way you'll be sure to be pushing to whatever history gets passed to 96 | `Router`. It also makes testing a bit easier since you can more easily 97 | stub context than singletons. 98 | 99 | --- 100 | 101 | [Next: Server Rendering](../13-server-rendering/) 102 | -------------------------------------------------------------------------------- /lessons/12-navigating/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, browserHistory, IndexRoute } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | import Home from './modules/Home' 9 | 10 | render(( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ), document.getElementById('app')) 21 | -------------------------------------------------------------------------------- /lessons/12-navigating/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/12-navigating/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • Home
    • 11 |
    • About
    • 12 |
    • Repos
    • 13 |
    14 | {this.props.children} 15 |
    16 | ) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /lessons/12-navigating/modules/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Home
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/12-navigating/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/12-navigating/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return ( 6 |
    7 |

    {this.props.params.repoName}

    8 |
    9 | ) 10 | } 11 | }) 12 | -------------------------------------------------------------------------------- /lessons/12-navigating/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    Repos

    9 |
      10 |
    • React Router
    • 11 |
    • React
    • 12 |
    13 | {this.props.children} 14 |
    15 | ) 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /lessons/12-navigating/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev", 8 | "start:dev": "webpack-dev-server --inline --content-base public/ --history-api-fallback", 9 | "start:prod": "webpack && node server.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "compression": "^1.6.1", 15 | "express": "^4.13.4", 16 | "if-env": "^1.0.0", 17 | "react": "^0.14.7", 18 | "react-dom": "^0.14.7", 19 | "react-router": "^2.0.0" 20 | }, 21 | "devDependencies": { 22 | "babel-core": "^6.5.1", 23 | "babel-loader": "^6.2.2", 24 | "babel-preset-es2015": "^6.5.0", 25 | "babel-preset-react": "^6.5.0", 26 | "http-server": "^0.8.5", 27 | "webpack": "^1.12.13", 28 | "webpack-dev-server": "^1.14.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lessons/12-navigating/public/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/12-navigating/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/12-navigating/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var path = require('path') 3 | var compression = require('compression') 4 | 5 | var app = express() 6 | 7 | app.use(compression()) 8 | 9 | // serve our static stuff like index.css 10 | app.use(express.static(path.join(__dirname, 'public'))) 11 | 12 | // send all requests to index.html so browserHistory works 13 | app.get('*', function (req, res) { 14 | res.sendFile(path.join(__dirname, 'public', 'index.html')) 15 | }) 16 | 17 | var PORT = process.env.PORT || 8080 18 | app.listen(PORT, function() { 19 | console.log('Production Express server running at localhost:' + PORT) 20 | }) 21 | -------------------------------------------------------------------------------- /lessons/12-navigating/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | 6 | output: { 7 | path: 'public', 8 | filename: 'bundle.js', 9 | publicPath: '/' 10 | }, 11 | 12 | plugins: process.env.NODE_ENV === 'production' ? [ 13 | new webpack.optimize.DedupePlugin(), 14 | new webpack.optimize.OccurrenceOrderPlugin(), 15 | new webpack.optimize.UglifyJsPlugin() 16 | ] : [], 17 | 18 | module: { 19 | loaders: [ 20 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/README.md: -------------------------------------------------------------------------------- 1 | # Server Rendering 2 | 3 | Alright, first things first. Server rendering, at its core is a simple 4 | concept in React. 5 | 6 | ```js 7 | render(, domNode) 8 | // can be rendered on the server as 9 | const markup = renderToString() 10 | ``` 11 | 12 | It's not rocket science, but it also isn't trivial. First I'm going to 13 | just throw a bunch of webpack shenanigans at you with little 14 | explanation, then we'll talk about the Router. 15 | 16 | Since node doesn't (and shouldn't) understand JSX, we need to compile 17 | the code somehow. Using something like `babel/register` is not fit for 18 | production use, so we'll use webpack to build a server bundle, just like 19 | we use it to build a client bundle. 20 | 21 | Make a new file called `webpack.server.config.js` and put this stuff in 22 | there: 23 | 24 | ```js 25 | var fs = require('fs') 26 | var path = require('path') 27 | 28 | module.exports = { 29 | 30 | entry: path.resolve(__dirname, 'server.js'), 31 | 32 | output: { 33 | filename: 'server.bundle.js' 34 | }, 35 | 36 | target: 'node', 37 | 38 | // keep node_module paths out of the bundle 39 | externals: fs.readdirSync(path.resolve(__dirname, 'node_modules')).concat([ 40 | 'react-dom/server', 'react/addons', 41 | ]).reduce(function (ext, mod) { 42 | ext[mod] = 'commonjs ' + mod 43 | return ext 44 | }, {}), 45 | 46 | node: { 47 | __filename: true, 48 | __dirname: true 49 | }, 50 | 51 | module: { 52 | loaders: [ 53 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 54 | ] 55 | } 56 | 57 | } 58 | ``` 59 | 60 | Hopefully some of that makes sense, we aren't going to cover what all of 61 | that stuff does, it's sufficient to say that now we can run our 62 | `server.js` file through webpack and then run it. 63 | 64 | Now we need to make some scripts to build server bundle before we try to 65 | run our app. Update your `package.json` script config to look like 66 | this: 67 | 68 | ``` 69 | "scripts": { 70 | "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev", 71 | "start:dev": "webpack-dev-server --inline --content-base public/ --history-api-fallback", 72 | "start:prod": "npm run build && node server.bundle.js", 73 | "build:client": "webpack", 74 | "build:server": "webpack --config webpack.server.config.js", 75 | "build": "npm run build:client && npm run build:server" 76 | }, 77 | ``` 78 | 79 | Now when we run `NODE_ENV=production npm start` both the client and 80 | server bundles get created by Webpack. 81 | 82 | Okay, let's talk about the Router. We're going to need our routes split 83 | out into a module so that both the client and server entries can require 84 | it. Make a file at `modules/routes` and move your routes and components 85 | into it. 86 | 87 | ```js 88 | // modules/routes.js 89 | import React from 'react' 90 | import { Route, IndexRoute } from 'react-router' 91 | import App from './App' 92 | import About from './About' 93 | import Repos from './Repos' 94 | import Repo from './Repo' 95 | import Home from './Home' 96 | 97 | module.exports = ( 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | ) 106 | ``` 107 | 108 | ```js 109 | // index.js 110 | import React from 'react' 111 | import { render } from 'react-dom' 112 | import { Router, browserHistory } from 'react-router' 113 | // import routes and pass them into 114 | import routes from './modules/routes' 115 | 116 | render( 117 | , 118 | document.getElementById('app') 119 | ) 120 | ``` 121 | 122 | Now open up `server.js`. We're going to bring in two modules from React 123 | Router to help us render on the server. 124 | 125 | If we tried to render a `` on the server like we do in the 126 | client, we'd get an empty screen since server rendering is synchronous 127 | and route matching is asynchronous. 128 | 129 | Also, most apps will want to use the router to help them load data, so 130 | asynchronous routes or not, you'll want to know what screens are going 131 | to render before you actually render so you can use that information to 132 | load asynchronous data before rendering. We don't have any data loading 133 | in this app, but you'll see where it could happen. 134 | 135 | First we import `match` and `RouterContext` from react router, then 136 | we'll match the routes to the url, and finally render. 137 | 138 | ```js 139 | // ... 140 | // import some new stuff 141 | import React from 'react' 142 | // we'll use this to render our app to an html string 143 | import { renderToString } from 'react-dom/server' 144 | // and these to match the url to routes and then render 145 | import { match, RouterContext } from 'react-router' 146 | import routes from './modules/routes' 147 | 148 | // ... 149 | 150 | // send all requests to index.html so browserHistory works 151 | 152 | app.get('*', (req, res) => { 153 | // match the routes to the url 154 | match({ routes: routes, location: req.url }, (err, redirect, props) => { 155 | // `RouterContext` is what the `Router` renders. `Router` keeps these 156 | // `props` in its state as it listens to `browserHistory`. But on the 157 | // server our app is stateless, so we need to use `match` to 158 | // get these props before rendering. 159 | const appHtml = renderToString() 160 | 161 | // dump the HTML into a template, lots of ways to do this, but none are 162 | // really influenced by React Router, so we're just using a little 163 | // function, `renderPage` 164 | res.send(renderPage(appHtml)) 165 | }) 166 | }) 167 | 168 | function renderPage(appHtml) { 169 | return ` 170 | 171 | 172 | 173 | My First React Router App 174 | 175 |
    ${appHtml}
    176 | 177 | ` 178 | } 179 | 180 | var PORT = process.env.PORT || 8080 181 | app.listen(PORT, function() { 182 | console.log('Production Express server running at localhost:' + PORT) 183 | }) 184 | ``` 185 | 186 | And that's it. Now if you run `NODE_ENV=production npm start` and visit 187 | the app, you can view source and see that the server is sending down our 188 | app to the browser. As you click around, you'll notice the client app 189 | has taken over and doesn't make requests to the server for UI. Pretty 190 | cool yeah?! 191 | 192 | 193 | Our callback to match is a little naive, here's what a production 194 | version would look like: 195 | 196 | ```js 197 | app.get('*', (req, res) => { 198 | match({ routes: routes, location: req.url }, (err, redirect, props) => { 199 | // in here we can make some decisions all at once 200 | if (err) { 201 | // there was an error somewhere during route matching 202 | res.status(500).send(err.message) 203 | } else if (redirect) { 204 | // we haven't talked about `onEnter` hooks on routes, but before a 205 | // route is entered, it can redirect. Here we handle on the server. 206 | res.redirect(redirect.pathname + redirect.search) 207 | } else if (props) { 208 | // if we got props then we matched a route and can render 209 | const appHtml = renderToString() 210 | res.send(renderPage(appHtml)) 211 | } else { 212 | // no errors, no redirect, we just didn't match anything 213 | res.status(404).send('Not Found') 214 | } 215 | }) 216 | }) 217 | ``` 218 | 219 | Server rendering is really new. There aren't really "best practices" 220 | yet, especially when it comes to data loading, so this tutorial is done, 221 | dropping you off at the bleeding edge. 222 | 223 | --- 224 | 225 | [Next: What's Next?](../14-whats-next/) 226 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, Route, browserHistory, IndexRoute } from 'react-router' 4 | import App from './modules/App' 5 | import About from './modules/About' 6 | import Repos from './modules/Repos' 7 | import Repo from './modules/Repo' 8 | import Home from './modules/Home' 9 | 10 | render(( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | ), document.getElementById('app')) 21 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • Home
    • 11 |
    • About
    • 12 |
    • Repos
    • 13 |
    14 | {this.props.children} 15 |
    16 | ) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/modules/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Home
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | const { userName, repoName } = this.props.params 6 | return ( 7 |
    8 |

    {userName} / {repoName}

    9 |
    10 | ) 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | contextTypes: { 6 | router: React.PropTypes.object 7 | }, 8 | 9 | handleSubmit(event) { 10 | event.preventDefault() 11 | const userName = event.target.elements[0].value 12 | const repo = event.target.elements[1].value 13 | const path = `/repos/${userName}/${repo}` 14 | this.context.router.push(path) 15 | }, 16 | 17 | render() { 18 | return ( 19 |
    20 |

    Repos

    21 |
      22 |
    • React Router
    • 23 |
    • React
    • 24 |
    • 25 |
      26 | / {' '} 27 | {' '} 28 | 29 |
      30 |
    • 31 |
    32 | {this.props.children} 33 |
    34 | ) 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev", 8 | "start:dev": "webpack-dev-server --inline --content-base public/ --history-api-fallback", 9 | "start:prod": "webpack && node server.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "compression": "^1.6.1", 15 | "express": "^4.13.4", 16 | "if-env": "^1.0.0", 17 | "react": "^0.14.7", 18 | "react-dom": "^0.14.7", 19 | "react-router": "^2.0.0" 20 | }, 21 | "devDependencies": { 22 | "babel-core": "^6.5.1", 23 | "babel-loader": "^6.2.2", 24 | "babel-preset-es2015": "^6.5.0", 25 | "babel-preset-react": "^6.5.0", 26 | "http-server": "^0.8.5", 27 | "webpack": "^1.12.13", 28 | "webpack-dev-server": "^1.14.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/public/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My First React Router App 5 | 6 |
    7 | 8 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/server.js: -------------------------------------------------------------------------------- 1 | var express = require('express') 2 | var path = require('path') 3 | var compression = require('compression') 4 | 5 | var app = express() 6 | 7 | app.use(compression()) 8 | 9 | // serve our static stuff like index.css 10 | app.use(express.static(path.join(__dirname, 'public'))) 11 | 12 | // send all requests to index.html so browserHistory works 13 | app.get('*', function (req, res) { 14 | res.sendFile(path.join(__dirname, 'public', 'index.html')) 15 | }) 16 | 17 | var PORT = process.env.PORT || 8080 18 | app.listen(PORT, function() { 19 | console.log('Production Express server running at localhost:' + PORT) 20 | }) 21 | -------------------------------------------------------------------------------- /lessons/13-server-rendering/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | 6 | output: { 7 | path: 'public', 8 | filename: 'bundle.js', 9 | publicPath: '/' 10 | }, 11 | 12 | plugins: process.env.NODE_ENV === 'production' ? [ 13 | new webpack.optimize.DedupePlugin(), 14 | new webpack.optimize.OccurrenceOrderPlugin(), 15 | new webpack.optimize.UglifyJsPlugin() 16 | ] : [], 17 | 18 | module: { 19 | loaders: [ 20 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lessons/14-whats-next/README.md: -------------------------------------------------------------------------------- 1 | # What's Next? 2 | 3 | Thanks for sticking with this tutorial all the way to the end! 4 | 5 | If you want to learn more: 6 | 7 | 1. Browse and run the [examples](https://github.com/reactjs/react-router/tree/latest/examples) 8 | - Read the [docs](https://github.com/reactjs/react-router/tree/latest/docs) 9 | - Read the source. 10 | 11 | Happy routing! 12 | 13 | -------------------------------------------------------------------------------- /lessons/14-whats-next/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render } from 'react-dom' 3 | import { Router, browserHistory } from 'react-router' 4 | import routes from './modules/routes' 5 | 6 | render( 7 | , 8 | document.getElementById('app') 9 | ) 10 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    About
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | render() { 6 | return ( 7 |
    8 |

    React Router Tutorial

    9 |
      10 |
    • Home
    • 11 |
    • About
    • 12 |
    • Repos
    • 13 |
    14 | {this.props.children} 15 |
    16 | ) 17 | } 18 | }) 19 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | return
    Home
    6 | } 7 | }) 8 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/NavLink.js: -------------------------------------------------------------------------------- 1 | // modules/NavLink.js 2 | import React from 'react' 3 | import { Link } from 'react-router' 4 | 5 | export default React.createClass({ 6 | render() { 7 | return 8 | } 9 | }) 10 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/Repo.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | export default React.createClass({ 4 | render() { 5 | const { userName, repoName } = this.props.params 6 | return ( 7 |
    8 |

    {userName} / {repoName}

    9 |
    10 | ) 11 | } 12 | }) 13 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/Repos.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import NavLink from './NavLink' 3 | 4 | export default React.createClass({ 5 | contextTypes: { 6 | router: React.PropTypes.object 7 | }, 8 | 9 | handleSubmit(event) { 10 | event.preventDefault() 11 | const userName = event.target.elements[0].value 12 | const repo = event.target.elements[1].value 13 | const path = `/repos/${userName}/${repo}` 14 | this.context.router.push(path) 15 | }, 16 | 17 | render() { 18 | return ( 19 |
    20 |

    Repos

    21 |
      22 |
    • React Router
    • 23 |
    • React
    • 24 |
    • 25 |
      26 | / {' '} 27 | {' '} 28 | 29 |
      30 |
    • 31 |
    32 | {this.props.children} 33 |
    34 | ) 35 | } 36 | }) 37 | -------------------------------------------------------------------------------- /lessons/14-whats-next/modules/routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Route, IndexRoute } from 'react-router' 3 | import App from './App' 4 | import About from './About' 5 | import Repos from './Repos' 6 | import Repo from './Repo' 7 | import Home from './Home' 8 | 9 | module.exports = ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | -------------------------------------------------------------------------------- /lessons/14-whats-next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "if-env NODE_ENV=production && npm run start:prod || npm run start:dev", 8 | "start:dev": "webpack-dev-server --inline --content-base public/ --history-api-fallback", 9 | "start:prod": "npm run build && node server.bundle.js", 10 | "build:client": "webpack", 11 | "build:server": "webpack --config webpack.server.config.js", 12 | "build": "npm run build:client && npm run build:server" 13 | }, 14 | "author": "", 15 | "license": "ISC", 16 | "dependencies": { 17 | "compression": "^1.6.1", 18 | "express": "^4.13.4", 19 | "if-env": "^1.0.0", 20 | "react": "^0.14.7", 21 | "react-dom": "^0.14.7", 22 | "react-router": "^2.0.0" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.5.1", 26 | "babel-loader": "^6.2.2", 27 | "babel-preset-es2015": "^6.5.0", 28 | "babel-preset-react": "^6.5.0", 29 | "http-server": "^0.8.5", 30 | "webpack": "^1.12.13", 31 | "webpack-dev-server": "^1.14.1" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lessons/14-whats-next/public/index.css: -------------------------------------------------------------------------------- 1 | .active { 2 | color: green; 3 | } 4 | -------------------------------------------------------------------------------- /lessons/14-whats-next/server.js: -------------------------------------------------------------------------------- 1 | import express from 'express' 2 | import path from 'path' 3 | import compression from 'compression' 4 | import React from 'react' 5 | import { renderToString } from 'react-dom/server' 6 | import { match, RouterContext } from 'react-router' 7 | import routes from './modules/routes' 8 | 9 | var app = express() 10 | 11 | app.use(compression()) 12 | 13 | // serve our static stuff like index.css 14 | app.use(express.static(path.join(__dirname, 'public'), {index: false})) 15 | 16 | // send all requests to index.html so browserHistory works 17 | app.get('*', (req, res) => { 18 | match({ routes, location: req.url }, (err, redirect, props) => { 19 | if (err) { 20 | res.status(500).send(err.message) 21 | } else if (redirect) { 22 | res.redirect(redirect.pathname + redirect.search) 23 | } else if (props) { 24 | // hey we made it! 25 | const appHtml = renderToString() 26 | res.send(renderPage(appHtml)) 27 | } else { 28 | res.status(404).send('Not Found') 29 | } 30 | }) 31 | }) 32 | 33 | function renderPage(appHtml) { 34 | return ` 35 | 36 | 37 | 38 | My First React Router App 39 | 40 |
    ${appHtml}
    41 | 42 | ` 43 | } 44 | 45 | var PORT = process.env.PORT || 8080 46 | app.listen(PORT, function() { 47 | console.log('Production Express server running at localhost:' + PORT) 48 | }) 49 | -------------------------------------------------------------------------------- /lessons/14-whats-next/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require('webpack') 2 | 3 | module.exports = { 4 | entry: './index.js', 5 | 6 | output: { 7 | path: 'public', 8 | filename: 'bundle.js', 9 | publicPath: '/' 10 | }, 11 | 12 | plugins: process.env.NODE_ENV === 'production' ? [ 13 | new webpack.optimize.DedupePlugin(), 14 | new webpack.optimize.OccurrenceOrderPlugin(), 15 | new webpack.optimize.UglifyJsPlugin() 16 | ] : [], 17 | 18 | module: { 19 | loaders: [ 20 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 21 | ] 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lessons/14-whats-next/webpack.server.config.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var path = require('path') 3 | 4 | module.exports = { 5 | 6 | entry: path.resolve(__dirname, 'server.js'), 7 | 8 | output: { 9 | filename: 'server.bundle.js' 10 | }, 11 | 12 | target: 'node', 13 | 14 | // keep node_module paths out of the bundle 15 | externals: fs.readdirSync(path.resolve(__dirname, 'node_modules')).concat([ 16 | 'react-dom/server' 17 | ]).reduce(function (ext, mod) { 18 | ext[mod] = 'commonjs ' + mod 19 | return ext 20 | }, {}), 21 | 22 | node: { 23 | __filename: false, 24 | __dirname: false 25 | }, 26 | 27 | module: { 28 | loaders: [ 29 | { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?presets[]=es2015&presets[]=react' } 30 | ] 31 | } 32 | 33 | } 34 | --------------------------------------------------------------------------------