From 94c72bd41ff16a85e943b7de009a85ec7bdb8f71 Mon Sep 17 00:00:00 2001 From: Justin Gordon Date: Sun, 31 Jul 2016 20:46:17 -1000 Subject: [PATCH] Fix express server rendering * Had to work around no shared store support * See https://github.com/shakacode/react_on_rails/issues/504 --- Procfile.dev | 9 ++--- Procfile.express | 8 +++++ README.md | 27 +++++++++----- .../startup/ClientRouterAppExpress.jsx | 35 +++++++++++++++++++ client/index.pug | 11 +++++- client/server-express.js | 4 +-- client/webpack.client.express.config.js | 4 +++ 7 files changed, 80 insertions(+), 18 deletions(-) create mode 100644 client/app/bundles/comments/startup/ClientRouterAppExpress.jsx diff --git a/Procfile.dev b/Procfile.dev index f8eb2675..0404e3c1 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,6 +1,6 @@ # Basic procfile for dev work. -# Runs all processes. Development is faster if you pick one of the other Procfiles if you don't need -# some of the processes: Procfile.hot or Procfile.express +# Development is faster if you pick one of the other Procfiles if you don't need +# the processes to create the test files. Thus, run the `Procfile.hot` one instead # Development rails requires both rails and rails-assets # (and rails-server-assets if server rendering) @@ -14,8 +14,3 @@ rails-static-client-assets: sh -c 'npm run build:dev:client' # Render static client assets. Remove if not server rendering rails-static-server-assets: sh -c 'npm run build:dev:server' - -# Run an express server if you want to mock out your endpoints. No Rails involved! -# Disable this if you are not using it. -# It's a great way to prototype UI especially with non-Rails developers! -express: sh -c 'HOT_PORT=4000 npm start' diff --git a/Procfile.express b/Procfile.express index c2c8471f..7b3321c0 100644 --- a/Procfile.express +++ b/Procfile.express @@ -4,4 +4,12 @@ # You can still run tests, and they will build the webpack file for each test run. # Hot reloading of JS and CSS is enabled! +# UPDATE: 2016-07-31 +# We no longer recommend using an express server with Rails. It's simply not necessary because: +# 1. Rails can hot reload +# 2. There's extra maintenance in keeping this synchronized. +# 3. React on Rails does not have a shared_store JS rendering: +# https://github.com/shakacode/react_on_rails/issues/504 +# Nevertheless, here's this working: + express: sh -c 'HOT_PORT=4000 npm start' diff --git a/README.md b/README.md index cdb5af3a..894e4c87 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ By Justin Gordon and the Shaka Code Team, [www.shakacode.com](http://www.shakaco An outdated full tutorial article behind of the motivation of this system can be found at: [Fast Rich Client Rails Development With Webpack and the ES6 Transpiler](http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/). Note, this source code repository is way ahead of the tutorial. # NEWS -We have not yet updated the `react_on_rails` gem generators for the following tasks. We're looking for help to migrate this, if you're interested in contributing to the project. *The tutorial* refers to this repo. The following changes have resulted in lots of differences for the webpack files and visual assets: +We have not yet updated the `react_on_rails` gem generators for the following tasks, and we probably never will. *The tutorial* refers to this repo. The following changes have resulted in lots of differences for the webpack files and visual assets: 1. NOTE: Any references to localhost:3000 *might* need to use 0.0.0.0:3000 until Puma fixes an issue regarding this. 1. **Handling of Sass and Bootstrap**: The tutorial uses CSS modules via Webpack. This is totally different than the older way of having Rails handle Sass/Bootstrap, and having NPM/Webpack handle the Webpack Dev Server. The tutorial now has NPM handle all visual assets. We are using this technique on a new app, and it's awesome! @@ -81,9 +81,6 @@ See package.json and Gemfile for versions 1. `foreman start -f Procfile.hot` 1. Open a browser tab to http://localhost:3000 for the Rails app example with HOT RELOADING 2. Try Hot Reloading steps below! -1. `foreman start -f Procfile.express` - 1. Open a browser tab to http://localhost:4000 for the Hot Module Replacement Example just using an express server (no Rails involved). This is good for fast prototyping of React components. However, this setup is not as useful now that we have hot reloading working for Rails! - 2. Try Hot Reloading steps below! 1. `foreman start -f Procfile.static` 1. Open a browser tab to http://localhost:3000 for the Rails app example. 2. When you make changes, you have to refresh the browser page. @@ -98,7 +95,6 @@ See package.json and Gemfile for versions 1. See all npm commands: `npm run` 1. Start all development processes: `foreman start -f Procfile.dev` 1. Start all Rails only development processes: `foreman start -f Procfile.hot` -1. Start development without Rails, using the Webpack Dev Server only: `npm start` (or `foreman start -f Procfile.express`) # Javascript development without Rails using the Webpack Dev Server @@ -138,7 +134,7 @@ line in the `rails_helper.rb` file. If you are using this project as an example ## Config Files - `webpack.client.base.config.js`: Common **client** configuration file to minimize code duplication for `webpack.client.rails.build.config`, `webpack.client.rails.hot.config`, `webpack.client.express.config` -- `webpack.client.express.config.js`: Webpack configuration for [client/server-express.js](client/server-express.js) +- `webpack.client.express.config.js`: Webpack configuration for Express server [client/server-express.js](client/server-express.js) - `webpack.client.rails.build.config.js`: Client side js bundle for deployment and tests. - `webpack.client.rails.hot.config.js`: Webpack Dev Server bundler for serving rails assets on port 3500, used by [client/server-rails-hot.js](client/server-rails-hot.js), for hot reloading JS and CSS within Rails. - `webpack.server.rails.build.config.js`: Server side js bundle, used by server rendering. @@ -180,11 +176,26 @@ bundle exec foreman start -f 1. [`Procfile.dev`](Procfile.dev): Starts the Webpack Dev Server and Rails with Hot Reloading. 2. [`Procfile.hot`](Procfile.hot): Starts the Rails server and the webpack server to provide hot reloading of assets, JavaScript and CSS. 3. [`Procfile.static`](Procfile.static): Starts the Rails server and generates static assets that are used for tests. -4. [`Procfile.express`](Procfile.express): Starts only the Webpack Dev Server. 5. [`Procfile.spec`](Procfile.spec): Starts webpack to create the static files for tests. **Good to know:** If you want to start `rails s` separately to debug in `pry`, then run `Procfile.spec` to generate the assets and run `rails s` in a separate console. 6. [`Procfile.static.trace`](Procfile.static.trace): Same as `Procfile.static` but prints tracing information useful for debugging server rendering. +4. [`Procfile.express`](Procfile.express): Starts only the Webpack Dev Server for rendering your components with only an Express server. + +In general, you want to avoid running more webpack watch processes than you need. + +## Rendering With an Express Server +UPDATE: 2016-07-31 + +We no longer recommend using an express server with Rails. It's simply not necessary because: + +1. Rails can hot reload +2. There's extra maintenance in keeping this synchronized. +3. React on Rails does not have a shared_store JS rendering, per [issue #504](https://github.com/shakacode/react_on_rails/issues/504) + +### Here's how to run the express server setup -In general, you want to avoid running more webpack watch processes than you need. The `Procfile.dev`, for example, runs both the express server (Webpack dev server) and the Rails hot assets reloading server. +1. `foreman start -f Procfile.express` +2. Open a browser tab to http://localhost:4000 for the Hot Module Replacement Example just using an express server (no Rails involved). This is good for fast prototyping of React components. However, this setup is not as useful now that we have hot reloading working for Rails! +3. Try Hot Reloading steps below! ## Contributors [The Shaka Code team!](http://www.shakacode.com/about/), led by [Justin Gordon](https://github.com/justin808/), along with with many others. See [contributors.md](docs/contributors.md) diff --git a/client/app/bundles/comments/startup/ClientRouterAppExpress.jsx b/client/app/bundles/comments/startup/ClientRouterAppExpress.jsx new file mode 100644 index 00000000..82a0e47c --- /dev/null +++ b/client/app/bundles/comments/startup/ClientRouterAppExpress.jsx @@ -0,0 +1,35 @@ +// Compare to ../ServerRouterApp.jsx +// This +import React from 'react'; +import { Provider } from 'react-redux'; +import ReactOnRails from 'react-on-rails'; +import { Router, browserHistory } from 'react-router'; +import routes from '../routes/routes'; +import { syncHistoryWithStore } from 'react-router-redux'; + +// No need for this file if using shared stores, but to make this example work for simple express +// server rendering, we only try get the shared store if the props were not passed. +// See https://github.com/shakacode/react_on_rails/issues/504 +import createStore from '../store/routerCommentsStore'; + +const RouterAppExpress = (_props, _railsContext) => { + + // See comment above + const store = createStore(_props); + + // Create an enhanced history that syncs navigation events with the store + const history = syncHistoryWithStore( + browserHistory, + store + ); + + return ( + + + + ); +}; + +ReactOnRails.register({ + RouterAppExpress, +}); diff --git a/client/index.pug b/client/index.pug index bb7c6899..af52090f 100644 --- a/client/index.pug +++ b/client/index.pug @@ -10,5 +10,14 @@ html script(src="vendor-bundle.js") script(src="app-bundle.js") + + // Next call is not available. + // https://github.com/shakacode/react_on_rails/issues/504 + // script. + // ReactOnRails.reduxStore('routerCommentsStore', !{props}); + // script. + // ReactOnRails.render('RouterApp', !{}, 'app'); + // + // Instead, we'll pass the props and special case in startup script. - ReactOnRails.render("RouterApp", !{props}, 'app'); + ReactOnRails.render('RouterAppExpress', !{props}, 'app'); diff --git a/client/server-express.js b/client/server-express.js index 7f5c3a84..17bd3fa5 100644 --- a/client/server-express.js +++ b/client/server-express.js @@ -31,7 +31,7 @@ server.app.use(bodyParser.urlencoded({ extended: true })); server.app.get('/comments.json', (req, res) => { sleep.sleep(1); res.setHeader('Content-Type', 'application/json'); - res.send(JSON.stringify(comments)); + res.send(JSON.stringify({ comments })); }); server.app.post('/comments.json', (req, res) => { @@ -49,7 +49,7 @@ server.app.post('/comments.json', (req, res) => { server.app.use('/', (req, res) => { var locals = { - props: JSON.stringify(comments), + props: JSON.stringify({ comments }), }; var layout = `${process.cwd()}/index.pug`; var html = pug.compileFile(layout, { pretty: true })(locals); diff --git a/client/webpack.client.express.config.js b/client/webpack.client.express.config.js index edbaf9ac..80b1ab8e 100644 --- a/client/webpack.client.express.config.js +++ b/client/webpack.client.express.config.js @@ -10,6 +10,10 @@ const hotPort = process.env.HOT_PORT || 4000; config.entry.vendor.push('bootstrap-loader'); config.entry.app.push( + // Shouldn't be necessary: + // https://github.com/shakacode/react_on_rails/issues/504 + './app/bundles/comments/startup/ClientRouterAppExpress', + // Webpack dev server `webpack-dev-server/client?http://localhost:${hotPort}`, 'webpack/hot/dev-server'