├── .gitignore ├── README.md ├── assets └── style.css ├── client.js ├── package.json └── server.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | assets/bundle.js 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-quickstart 2 | 3 | A minimal React project template which combines: 4 | 5 | * [react-router-component][] to provide HTML5 History routing and navigation 6 | 7 | * [react-async][] to create "asynchronous" React components 8 | 9 | * [express][] to serve pre-rendered React components, assets and provide API 10 | 11 | * [browserify][] to provide module system for a browser 12 | 13 | * [npm][] to install and manage server-side and client-side dependencies 14 | 15 | Every "page" in the application is **pre-rendered on server** so the user can 16 | see the UI before the client code is shipped to a browser. After that 17 | application starts functioning like a **single page application**, navigating 18 | between "pages" without reloads. 19 | 20 | ## Project structure 21 | 22 | Project structure is really minimal, you'd probably like to customize it for 23 | your specific needs and taste: 24 | 25 | . 26 | ├── assets 27 | ├── client.js 28 | ├── package.json 29 | └── server.js 30 | 31 | Directory `assets` is served under `/assets` URL, `client.js` module contains UI 32 | code while `server.js` — HTTP server which serves pre-rendered React components, 33 | assets and provide a stub for a REST API. 34 | 35 | ## Development workflow 36 | 37 | After cloning a git repo, run: 38 | 39 | % npm install 40 | 41 | to install all needed dependencies and then: 42 | 43 | % npm run start 44 | 45 | to start a development server. 46 | 47 | Now you can start edit the source code — on changes, server will be reloaded and 48 | client code bundle will be rebuilt. 49 | 50 | ## Going "production" 51 | 52 | To build an optimized bundle of client code run: 53 | 54 | % npm run build 55 | 56 | which will produce `assets/bundle.js` build, then: 57 | 58 | % npm run start-prod 59 | 60 | to start server in "production" mode (no source code watching and serving 61 | optimized bundle to browser). 62 | 63 | [react-router-component]: http://andreypopp.viewdocs.io/react-router-component 64 | [react-async]: http://andreypopp.viewdocs.io/react-async 65 | [express]: expressjs.com 66 | [npm]: https://www.npmjs.org/ 67 | [browserify]: http://browserify.org/ 68 | -------------------------------------------------------------------------------- /assets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: Helvetica, sans-serif; 5 | background: #fefefe; 6 | color: #444444; 7 | } 8 | 9 | .App { 10 | padding: 25px; 11 | } 12 | -------------------------------------------------------------------------------- /client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jsx React.DOM 3 | */ 4 | 'use strict'; 5 | 6 | 7 | var React = require('react'); 8 | var ReactAsync = require('react-async'); 9 | var ReactRouter = require('react-router-component'); 10 | var superagent = require('superagent'); 11 | 12 | var Pages = ReactRouter.Pages; 13 | var Page = ReactRouter.Page; 14 | var NotFound = ReactRouter.NotFound; 15 | var Link = ReactRouter.Link; 16 | 17 | var MainPage = React.createClass({ 18 | 19 | render: function() { 20 | return ( 21 |
22 |

Hello, anonymous!

23 |

Login

24 |
25 | ); 26 | } 27 | }); 28 | 29 | var UserPage = React.createClass({ 30 | mixins: [ReactAsync.Mixin], 31 | 32 | statics: { 33 | getUserInfo: function(username, cb) { 34 | /* 35 | * The use of localhost URLs work as long as the browser is running on the same machine as the server, 36 | * a typical development setup. 37 | * As soon as you want to run this code on public facing machines, each server will need to know it's 38 | * own hostname and port (which is ugly). 39 | * Relative paths cannot work for serverside rendering, as that has no page context. 40 | * More discussion of this issue, and solutions, can be found at: 41 | * https://github.com/andreypopp/react-async/issues/34 42 | * http://stackoverflow.com/questions/26463924/getting-rid-of-localhost3000-urls-for-reactasync 43 | */ 44 | superagent.get( 45 | 'http://localhost:3000/api/users/' + username, 46 | function(err, res) { 47 | cb(err, res ? res.body : null); 48 | }); 49 | } 50 | }, 51 | 52 | getInitialStateAsync: function(cb) { 53 | this.type.getUserInfo(this.props.username, cb); 54 | }, 55 | 56 | componentWillReceiveProps: function(nextProps) { 57 | if (this.props.username !== nextProps.username) { 58 | this.type.getUserInfo(nextProps.username, function(err, info) { 59 | if (err) { 60 | throw err; 61 | } 62 | this.setState(info); 63 | }.bind(this)); 64 | } 65 | }, 66 | 67 | render: function() { 68 | var otherUser = this.props.username === 'doe' ? 'ivan' : 'doe'; 69 | return ( 70 |
71 |

Hello, {this.state.name}!

72 |

73 | Go to /users/{otherUser} 74 |

75 |

Logout

76 |
77 | ); 78 | } 79 | }); 80 | 81 | var NotFoundHandler = React.createClass({ 82 | 83 | render: function() { 84 | return ( 85 |

Page not found

86 | ); 87 | } 88 | }); 89 | 90 | var App = React.createClass({ 91 | 92 | render: function() { 93 | return ( 94 | 95 | 96 | 97 |