react-on-rails
Advanced tools
Comparing version 6.0.5 to 6.1.0
@@ -6,27 +6,38 @@ # Change Log | ||
## [Unreleased] | ||
## [6.1.0] 2016-08-21 | ||
## [6.0.5] | ||
##### Added | ||
- Added better error messages to avoid issues with shared redux stores [#470](https://github.com/shakacode/react_on_rails/pull/470). | ||
- Node option for installer added as alternative for server rendering [#469](https://github.com/shakacode/react_on_rails/pull/469) by [jbhatab](https://github.com/jbhatab). | ||
- Server rendering now supports contexts outside of browser rendering, such as ActionMailer templates [#486](https://github.com/shakacode/react_on_rails/pull/486) by [eacaps](https://github.com/eacaps). | ||
- Added authenticityToken() and authenticityHeaders() javascript helpers for easier use when working with CSRF protection tag generated by Rails [#517](https://github.com/shakacode/react_on_rails/pull/517) by [dzirtusss](https://github.com/dzirtusss). | ||
- Updated JavaScript error handling on the client side. Errors in client rendering now pass through to the browser [#521](https://github.com/shakacode/react_on_rails/pull/521) by [dzirtusss](https://github.com/dzirtusss). | ||
## [6.0.4] | ||
##### Fixed | ||
- Added polyfill for clearTimeout which is used by babel-polyfill [#451](https://github.com/shakacode/react_on_rails/pull/451). | ||
- React on Rails now correctly parses single-digit version strings from package.json [#491](https://github.com/shakacode/react_on_rails/pull/491) by [samphilipd ](https://github.com/samphilipd). | ||
- Fixed assets symlinking to correctly use filenames with spaces. Begining in [#510](https://github.com/shakacode/react_on_rails/pull/510), ending in [#513](https://github.com/shakacode/react_on_rails/pull/513) by [dzirtusss](https://github.com/dzirtusss). | ||
- Check encoding of request's original URL and force it to UTF-8 [#527](https://github.com/shakacode/react_on_rails/pull/527) by [lucke84](https://github.com/lucke84) | ||
## [6.0.3] | ||
## [6.0.5] - 2016-07-11 | ||
##### Added | ||
- Added better error messages to avoid issues with shared redux stores [#470](https://github.com/shakacode/react_on_rails/pull/470) by by [justin808](https://github.com/justin808). | ||
## [6.0.4] - 2016-06-13 | ||
##### Fixed | ||
- Added polyfill for clearTimeout which is used by babel-polyfill [#451](https://github.com/shakacode/react_on_rails/pull/451) by [martyphee](https://github.com/martyphee) | ||
## [6.0.3] - 2016-06-07 | ||
##### Fixed | ||
- Added assets symlinking support on Heroku [#446](https://github.com/shakacode/react_on_rails/pull/446) by [Alexey Karasev](https://github.com/alleycat-at-git). | ||
## [6.0.2] | ||
## [6.0.2] - 2016-06-06 | ||
##### Fixed | ||
- Fix colisions in ids of DOM nodes generated by `react_component` by indexing in using an UUID rather than an auto-increment value. This means that it should be overriden using the `id` parameter of `react_component` if one wants to generate a predictable id (_e.g._ for testing purpose). See [Issue #437](https://github.com/shakacode/react_on_rails/issues/437). Fixed in [#438](https://github.com/shakacode/react_on_rails/pull/438) by [Michael Baudino](https://github.com/michaelbaudino). | ||
## [6.0.1] | ||
## [6.0.1] - 2016-05-27 | ||
##### Fixed | ||
- Allow for older version of manifest.json for older versions of sprockets. See [Issue #435](https://github.com/shakacode/react_on_rails/issues/435). Fixed in [#436](https://github.com/shakacode/react_on_rails/pull/436) by [alleycat-at-git](https://github.com/alleycat-at-git). | ||
## [6.0.0] | ||
## [6.0.0] - 2016-05-25 | ||
##### Breaking Changes | ||
- Added automatic compilation of assets at precompile is now done by ReactOnRails. Thus, you don't need to provide your own assets.rake file that does the precompilation. | ||
- Added automatic compilation of assets at precompile is now done by ReactOnRails. Thus, you don't need to provide your own assets.rake file that does the precompilation. | ||
[#398](https://github.com/shakacode/react_on_rails/pull/398) by [robwise](https://github.com/robwise), [jbhatab](https://github.com/jbhatab), and [justin808](https://github.com/justin808). | ||
@@ -37,3 +48,3 @@ - **Migration to v6** | ||
- See [shakacode/react-webpack-rails-tutorial/pull/287](https://github.com/shakacode/react-webpack-rails-tutorial/pull/287) for an example of upgrading from v5. | ||
- To configure the asset compliation you can either | ||
@@ -44,3 +55,3 @@ 1. Specify a `config/react_on_rails` setting for `npm_build_production_command` to be nil to turn this feature off. | ||
- If you are using the ReactOnRails test helper, then you will need to add the 'config.npm_build_test_command' to your config to tell react_on_rails what command to run when you run rspec. | ||
- See [shakacode/react-webpack-rails-tutorial #287](https://github.com/shakacode/react-webpack-rails-tutorial/pull/287/files) for an upgrade example. The PR has a few comments on the upgrade. | ||
@@ -95,6 +106,6 @@ | ||
##### Fixed | ||
- [Security] Address failure to sanitize console messages when server rendering and displaying in the browser console. See [#366](https://github.com/shakacode/react_on_rails/pull/366) and [#370](https://github.com/shakacode/react_on_rails/pull/370) by [justin808](https://github.com/justin808) | ||
- Security Fixes: Address failure to sanitize console messages when server rendering and displaying in the browser console. See [#366](https://github.com/shakacode/react_on_rails/pull/366) and [#370](https://github.com/shakacode/react_on_rails/pull/370) by [justin808](https://github.com/justin808) | ||
##### Added | ||
- railsContext includes the port number and a boolean if the code is being run on the server or client. | ||
- railsContext includes the port number and a boolean if the code is being run on the server or client. | ||
@@ -360,3 +371,4 @@ ## [5.1.0] - 2016-04-03 | ||
[Unreleased]: https://github.com/shakacode/react_on_rails/compare/6.0.5...master | ||
[Unreleased]: https://github.com/shakacode/react_on_rails/compare/6.1.0...master | ||
[6.1.0]: https://github.com/shakacode/react_on_rails/compare/6.0.5...6.1.0 | ||
[6.0.5]: https://github.com/shakacode/react_on_rails/compare/6.0.4...6.0.5 | ||
@@ -363,0 +375,0 @@ [6.0.4]: https://github.com/shakacode/react_on_rails/compare/6.0.3...6.0.4 |
@@ -22,6 +22,2 @@ 'use strict'; | ||
var _handleError = require('./handleError'); | ||
var _handleError2 = _interopRequireDefault(_handleError); | ||
var _isRouterResult = require('./isRouterResult'); | ||
@@ -33,3 +29,4 @@ | ||
var REACT_ON_RAILS_COMPONENT_CLASS_NAME = 'js-react-on-rails-component'; | ||
var REACT_ON_RAILS_COMPONENT_CLASS_NAME = 'js-react-on-rails-component'; /* global ReactOnRails Turbolinks */ | ||
var REACT_ON_RAILS_STORE_CLASS_NAME = 'js-react-on-rails-store'; | ||
@@ -57,10 +54,2 @@ | ||
function forEachComponent(fn, railsContext) { | ||
forEach(fn, REACT_ON_RAILS_COMPONENT_CLASS_NAME, railsContext); | ||
} | ||
function forEachStore(railsContext) { | ||
forEach(initializeStore, REACT_ON_RAILS_STORE_CLASS_NAME, railsContext); | ||
} | ||
function forEach(fn, className, railsContext) { | ||
@@ -73,4 +62,4 @@ var els = document.getElementsByClassName(className); | ||
function turbolinksVersion5() { | ||
return typeof Turbolinks.controller !== 'undefined'; | ||
function forEachComponent(fn, railsContext) { | ||
forEach(fn, REACT_ON_RAILS_COMPONENT_CLASS_NAME, railsContext); | ||
} | ||
@@ -86,2 +75,10 @@ | ||
function forEachStore(railsContext) { | ||
forEach(initializeStore, REACT_ON_RAILS_STORE_CLASS_NAME, railsContext); | ||
} | ||
function turbolinksVersion5() { | ||
return typeof Turbolinks.controller !== 'undefined'; | ||
} | ||
/** | ||
@@ -115,7 +112,4 @@ * Used for client rendering by ReactOnRails | ||
} catch (e) { | ||
(0, _handleError2.default)({ | ||
e: e, | ||
name: name, | ||
serverSide: false | ||
}); | ||
e.message = 'ReactOnRails encountered an error while rendering component: ' + name + '.' + ('Original message: ' + e.message); | ||
throw e; | ||
} | ||
@@ -128,5 +122,5 @@ } | ||
return JSON.parse(el.getAttribute('data-rails-context')); | ||
} else { | ||
return null; | ||
} | ||
return null; | ||
} | ||
@@ -162,2 +156,3 @@ | ||
// Tried with a file local variable, but the install handler gets called twice. | ||
// eslint-disable-next-line no-underscore-dangle | ||
if (context.__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__) { | ||
@@ -167,4 +162,4 @@ return; | ||
context.__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__ = // eslint-disable-line no-param-reassign | ||
true; | ||
// eslint-disable-next-line no-underscore-dangle | ||
context.__REACT_ON_RAILS_EVENT_HANDLERS_RAN_ONCE__ = true; | ||
@@ -181,14 +176,12 @@ debugTurbolinks('Adding DOMContentLoaded event to install event listeners.'); | ||
reactOnRailsPageLoaded(); | ||
} else if (turbolinksVersion5()) { | ||
debugTurbolinks('USING TURBOLINKS 5: document added event listeners turbolinks:before-render and ' + 'turbolinks:load.'); | ||
document.addEventListener('turbolinks:before-render', reactOnRailsPageUnloaded); | ||
document.addEventListener('turbolinks:load', reactOnRailsPageLoaded); | ||
} else { | ||
if (turbolinksVersion5()) { | ||
debugTurbolinks('USING TURBOLINKS 5: document added event listeners turbolinks:before-render and ' + 'turbolinks:load.'); | ||
document.addEventListener('turbolinks:before-render', reactOnRailsPageUnloaded); | ||
document.addEventListener('turbolinks:load', reactOnRailsPageLoaded); | ||
} else { | ||
debugTurbolinks('USING TURBOLINKS 2: document added event listeners page:before-unload and ' + 'page:change.'); | ||
document.addEventListener('page:before-unload', reactOnRailsPageUnloaded); | ||
document.addEventListener('page:change', reactOnRailsPageLoaded); | ||
} | ||
debugTurbolinks('USING TURBOLINKS 2: document added event listeners page:before-unload and ' + 'page:change.'); | ||
document.addEventListener('page:before-unload', reactOnRailsPageUnloaded); | ||
document.addEventListener('page:change', reactOnRailsPageLoaded); | ||
} | ||
}); | ||
} |
@@ -23,12 +23,7 @@ 'use strict'; | ||
var _context = require('./context'); | ||
var _context2 = _interopRequireDefault(_context); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// key = name used by react_on_rails | ||
var registeredComponents = new _map2.default(); // key = name used by react_on_rails | ||
// value = { name, component, generatorFunction: boolean } | ||
var _components = new _map2.default(); | ||
@@ -42,3 +37,3 @@ exports.default = { | ||
(0, _keys2.default)(components).forEach(function (name) { | ||
if (_components.has(name)) { | ||
if (registeredComponents.has(name)) { | ||
console.warn('Called register for component that is already registered', name); | ||
@@ -54,3 +49,3 @@ } | ||
_components.set(name, { | ||
registeredComponents.set(name, { | ||
name: name, | ||
@@ -69,8 +64,8 @@ component: component, | ||
get: function get(name) { | ||
if (_components.has(name)) { | ||
return _components.get(name); | ||
} else { | ||
var keys = (0, _from2.default)(_components.keys()).join(', '); | ||
throw new Error('Could not find component registered with name ' + name + '. Registered component names include [ ' + keys + ' ]. Maybe you forgot to register the component?'); | ||
if (registeredComponents.has(name)) { | ||
return registeredComponents.get(name); | ||
} | ||
var keys = (0, _from2.default)(registeredComponents.keys()).join(', '); | ||
throw new Error('Could not find component registered with name ' + name + '. Registered component names include [ ' + keys + ' ]. Maybe you forgot to register the component?'); | ||
}, | ||
@@ -85,4 +80,4 @@ | ||
components: function components() { | ||
return _components; | ||
return registeredComponents; | ||
} | ||
}; |
@@ -29,2 +29,4 @@ 'use strict'; | ||
*/ | ||
/* eslint-disable react/prop-types */ | ||
function createReactElement(_ref) { | ||
@@ -36,3 +38,2 @@ var name = _ref.name; | ||
var trace = _ref.trace; | ||
var location = _ref.location; | ||
@@ -39,0 +40,0 @@ if (trace) { |
@@ -47,3 +47,3 @@ 'use strict'; | ||
exports.default = function (options) { | ||
var handleError = function handleError(options) { | ||
var e = options.e; | ||
@@ -75,2 +75,6 @@ var jsCode = options.jsCode; | ||
} | ||
}; | ||
return undefined; | ||
}; | ||
exports.default = handleError; |
@@ -19,2 +19,6 @@ 'use strict'; | ||
var _reactDom = require('react-dom'); | ||
var _reactDom2 = _interopRequireDefault(_reactDom); | ||
var _clientStartup = require('./clientStartup'); | ||
@@ -48,5 +52,5 @@ | ||
var _reactDom = require('react-dom'); | ||
var _Authenticity = require('./Authenticity'); | ||
var _reactDom2 = _interopRequireDefault(_reactDom); | ||
var _Authenticity2 = _interopRequireDefault(_Authenticity); | ||
@@ -115,12 +119,20 @@ var _context = require('./context'); | ||
*/ | ||
setOptions: function setOptions(options) { | ||
if (options.hasOwnProperty('traceTurbolinks')) { | ||
this._options.traceTurbolinks = options.traceTurbolinks; | ||
delete options.traceTurbolinks; | ||
setOptions: function setOptions(newOptions) { | ||
if ('traceTurbolinks' in newOptions) { | ||
this.options.traceTurbolinks = newOptions.traceTurbolinks; | ||
delete newOptions.traceTurbolinks; | ||
} | ||
if ((0, _keys2.default)(options).length > 0) { | ||
throw new Error('Invalid options passed to ReactOnRails.options: ', (0, _stringify2.default)(options)); | ||
if ((0, _keys2.default)(newOptions).length > 0) { | ||
throw new Error('Invalid options passed to ReactOnRails.options: ', (0, _stringify2.default)(newOptions)); | ||
} | ||
}, | ||
/** | ||
* Allow directly calling the page loaded script in case the default events that trigger react | ||
* rendering are not sufficient, such as when loading JavaScript asynchronously with TurboLinks: | ||
* More details can be found here: | ||
* https://github.com/shakacode/react_on_rails/blob/master/docs/additional-reading/turbolinks.md | ||
*/ | ||
reactOnRailsPageLoaded: function reactOnRailsPageLoaded() { | ||
@@ -131,5 +143,28 @@ ClientStartup.reactOnRailsPageLoaded(); | ||
//////////////////////////////////////////////////////////////////////////////// | ||
/** | ||
* Returns CSRF authenticity token inserted by Rails csrf_meta_tags | ||
* @returns String or null | ||
*/ | ||
authenticityToken: function authenticityToken() { | ||
return _Authenticity2.default.authenticityToken(); | ||
}, | ||
/** | ||
* Returns header with csrf authenticity token and XMLHttpRequest | ||
* @param {*} other headers | ||
* @returns {*} header | ||
*/ | ||
authenticityHeaders: function authenticityHeaders() { | ||
var otherHeaders = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0]; | ||
return _Authenticity2.default.authenticityHeaders(otherHeaders); | ||
}, | ||
// ///////////////////////////////////////////////////////////////////////////// | ||
// INTERNALLY USED APIs | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// ///////////////////////////////////////////////////////////////////////////// | ||
@@ -142,3 +177,3 @@ /** | ||
option: function option(key) { | ||
return this._options[key]; | ||
return this.options[key]; | ||
}, | ||
@@ -182,2 +217,4 @@ | ||
var reactElement = (0, _createReactElement2.default)({ name: name, props: props, domNodeId: domNodeId }); | ||
// eslint-disable-next-line react/no-render-return-value | ||
return _reactDom2.default.render(reactElement, document.getElementById(domNodeId)); | ||
@@ -249,7 +286,7 @@ }, | ||
resetOptions: function resetOptions() { | ||
this._options = (0, _assign2.default)({}, DEFAULT_OPTIONS); | ||
this.options = (0, _assign2.default)({}, DEFAULT_OPTIONS); | ||
} | ||
}; | ||
ReactOnRails.resetOptions(); | ||
ctx.ReactOnRails.resetOptions(); | ||
@@ -256,0 +293,0 @@ ClientStartup.clientStartup(ctx); |
@@ -53,8 +53,6 @@ 'use strict'; | ||
console.error('React Router ERROR: ' + (0, _stringify2.default)(reactElementOrRouterResult.routeError)); | ||
} else { | ||
if (trace) { | ||
var redirectLocation = reactElementOrRouterResult.redirectLocation; | ||
var redirectPath = redirectLocation.pathname + redirectLocation.search; | ||
console.log('ROUTER REDIRECT: ' + name + ' to dom node with id: ' + domNodeId + ', redirect to ' + redirectPath); | ||
} | ||
} else if (trace) { | ||
var redirectLocation = reactElementOrRouterResult.redirectLocation; | ||
var redirectPath = redirectLocation.pathname + redirectLocation.search; | ||
console.log('ROUTER REDIRECT: ' + name + ' to dom node with id: ' + domNodeId + ', redirect to ' + redirectPath); | ||
} | ||
@@ -61,0 +59,0 @@ } else { |
@@ -19,13 +19,9 @@ 'use strict'; | ||
var _context = require('./context'); | ||
var _context2 = _interopRequireDefault(_context); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _storeGenerators = new _map2.default(); // key = name used by react_on_rails to identify the store | ||
// key = name used by react_on_rails to identify the store | ||
// value = redux store creator, which is a function that takes props and returns a store | ||
var registeredStoreGenerators = new _map2.default(); | ||
var hydratedStores = new _map2.default(); | ||
var _stores = new _map2.default(); | ||
exports.default = { | ||
@@ -39,3 +35,3 @@ /** | ||
(0, _keys2.default)(storeGenerators).forEach(function (name) { | ||
if (_storeGenerators.has(name)) { | ||
if (registeredStoreGenerators.has(name)) { | ||
console.warn('Called registerStore for store that is already registered', name); | ||
@@ -49,3 +45,3 @@ } | ||
_storeGenerators.set(name, store); | ||
registeredStoreGenerators.set(name, store); | ||
}); | ||
@@ -65,10 +61,10 @@ }, | ||
if (_stores.has(name)) { | ||
return _stores.get(name); | ||
if (hydratedStores.has(name)) { | ||
return hydratedStores.get(name); | ||
} | ||
var storeKeys = (0, _from2.default)(_stores.keys()).join(', '); | ||
var storeKeys = (0, _from2.default)(hydratedStores.keys()).join(', '); | ||
if (storeKeys.length === 0) { | ||
var msg = 'There are no stores hydrated and you are requesting the store ' + (name + '. This can happen if you are server rendering and you do not call ') + 'redux_store near the top of your controller action\'s view (not the layout) ' + 'and before any call to react_component.'; | ||
var msg = 'There are no stores hydrated and you are requesting the store ' + (name + '. This can happen if you are server rendering and you do not call ') + "redux_store near the top of your controller action's view (not the layout) " + 'and before any call to react_component.'; | ||
throw new Error(msg); | ||
@@ -81,2 +77,4 @@ } | ||
} | ||
return undefined; | ||
}, | ||
@@ -91,8 +89,8 @@ | ||
getStoreGenerator: function getStoreGenerator(name) { | ||
if (_storeGenerators.has(name)) { | ||
return _storeGenerators.get(name); | ||
} else { | ||
var storeKeys = (0, _from2.default)(_storeGenerators.keys()).join(', '); | ||
throw new Error('Could not find store registered with name \'' + name + '\'. Registered store ' + ('names include [ ' + storeKeys + ' ]. Maybe you forgot to register the store?')); | ||
if (registeredStoreGenerators.has(name)) { | ||
return registeredStoreGenerators.get(name); | ||
} | ||
var storeKeys = (0, _from2.default)(registeredStoreGenerators.keys()).join(', '); | ||
throw new Error('Could not find store registered with name \'' + name + '\'. Registered store ' + ('names include [ ' + storeKeys + ' ]. Maybe you forgot to register the store?')); | ||
}, | ||
@@ -107,3 +105,3 @@ | ||
setStore: function setStore(name, store) { | ||
_stores.set(name, store); | ||
hydratedStores.set(name, store); | ||
}, | ||
@@ -117,3 +115,3 @@ | ||
storeGenerators: function storeGenerators() { | ||
return _storeGenerators; | ||
return registeredStoreGenerators; | ||
}, | ||
@@ -127,4 +125,4 @@ | ||
stores: function stores() { | ||
return _stores; | ||
return hydratedStores; | ||
} | ||
}; |
{ | ||
"name": "react-on-rails", | ||
"version": "6.0.5", | ||
"version": "6.1.0", | ||
"description": "react-on-rails JavaScript for react_on_rails Ruby gem", | ||
@@ -23,5 +23,7 @@ "main": "node_package/lib/ReactOnRails.js", | ||
"blue-tape": "^0.2.0", | ||
"eslint": "^2.6.0", | ||
"eslint-config-shakacode": "4.0.0", | ||
"eslint-plugin-react": "^4.2.3", | ||
"eslint": "^3.3.1", | ||
"eslint-config-shakacode": "^6.0.0", | ||
"eslint-plugin-import": "^1.13.0", | ||
"eslint-plugin-jsx-a11y": "^2.1.0", | ||
"eslint-plugin-react": "^6.1.2", | ||
"jscs": "^2.11.0", | ||
@@ -51,5 +53,5 @@ "jsdom": "^8.2.0", | ||
"build": "node_package/scripts/build", | ||
"build-watch": "$(npm bin)/babel --watch --out-dir node_package/lib node_package/src", | ||
"eslint": "$(npm bin)/eslint . --ext .jsx and .js", | ||
"jscs": "$(npm bin)/jscs . -e -v", | ||
"build-watch": "babel --watch --out-dir node_package/lib node_package/src", | ||
"eslint": "eslint .", | ||
"jscs": "jscs -e -v .", | ||
"lint": "npm run eslint && npm run jscs", | ||
@@ -56,0 +58,0 @@ "lint:fix": "node_package/scripts/lint-fix", |
333
README.md
[](https://travis-ci.org/shakacode/react_on_rails) [](https://gemnasium.com/shakacode/react_on_rails) [](https://badge.fury.io/rb/react_on_rails) [](https://badge.fury.io/js/react-on-rails) [](https://codeclimate.com/github/shakacode/react_on_rails) [](https://coveralls.io/github/shakacode/react_on_rails?branch=master) | ||
**For a complete example of this gem, see our live demo at [www.reactrails.com](http://www.reactrails.com). ([Source Code](https://github.com/shakacode/react-webpack-rails-tutorial))** | ||
Aloha from Justin Gordon and the [ShakaCode](http://www.shakacode.com) Team! We need your help. Venture capital funding has slowed and, for the first time, my ShakaCode team is actively looking for our next project. If you like **React on Rails**, please consider contacting me if we could potentially help you in any way. I'm offering a free half hour project consultation, on anything from React on Rails to any aspect of web application development, including both consumer and enterprise products. You can read more about my background [here](http://www.railsonmaui.com/about). Whether you have a new project, or need help on an existing project, please email me directly at [justin@shakacode.com](mailto:justin@shakacode.com). And thanks in advance for any referrals! Your support keeps this project going. | ||
Aloha from Justin Gordon ([bio](http://www.railsonmaui.com/about)) and the [ShakaCode](http://www.shakacode.com) Team! We're actively looking for new projects. If you like **React on Rails**, please consider contacting me at [justin@shakacode.com](mailto:justin@shakacode.com) if we could potentially help you in any way. Besides consulting on bigger projects, [ShakaCode](http://www.shakacode.com) is doing Skype plus Slack/Github based coaching for React on Rails. [Click here](http://www.shakacode.com/work/index.html) for more information. | ||
# NOTES | ||
* Besides consulting on bigger projects, [ShakaCode](http://www.shakacode.com) is doing Skype plus Slack/Github based coaching for "React on Rails". [Click here](http://www.shakacode.com/work/index.html) for more information. | ||
* See our article [The React on Rails Doctrine](https://medium.com/@railsonmaui/the-react-on-rails-doctrine-3c59a778c724) and see [slides on React on Rails](http://www.slideshare.net/justingordon/react-on-rails-v4032). | ||
* For a complete example, see the [React Webpack Rails Tutorial Code](https://github.com/shakacode/react-webpack-rails-tutorial) along with the live example at [www.reactrails.com](http://www.reactrails.com). | ||
* The generator of React on Rails does not setup CSS modules and hot reloading via the Rails server as is demonstrated in the [shakacode/react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/). *We do support this, but we don't generate the code.* If you did generate a fresh app from react_on_rails and want to move to CSS Modules, then see [PR 175: Babel 6 / CSS Modules / Rails hot reloading](https://github.com/shakacode/react-webpack-rails-tutorial/pull/175). Note, while there are probably fixes after this PR was accepted, this has the majority of the changes. See [the tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/#news) for more information. For more information on how to setup hot reloading in a Rails app, see [Hot Reloading of Assets For Rails Development](docs/additional-reading/hot-reloading-rails-development.md). | ||
* See [Projects](PROJECTS.md) using and [KUDOS](./KUDOS.md) for React on Rails. Please submit yours! Please edit either page or [email us](mailto:contact@shakacode.com) and we'll add your info. We also **love stars** as it helps us attract new users and contributors. | ||
* On Twitter, follow [@railsonmaui](https://twitter.com/railsonmaui) and [@shakacode](https://twitter.com/shakacode) for updates on releases. | ||
We're offering a free half-hour project consultation, on anything from React on Rails to any aspect of web application development for both consumer and enterprise products. In addition to React.js and Rails, we're doing react-native iOS and Android apps! | ||
Whether you have a new project or need help on an existing project, feel free to contact me directly at [justin@shakacode.com](mailto:justin@shakacode.com) and thanks in advance for any referrals! | ||
Your support keeps this project going. | ||
(Want to become a contributor? [Contact us](mailto:contact@shakacode.com) for an Slack team invite! Also, see ["easy" issues](https://github.com/shakacode/react_on_rails/labels/easy) and [issues for the full tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/issues?q=is%3Aissue+is%3Aopen+label%3Aeasy).) | ||
# NEWS | ||
* 2016-06-13: 6.0.4 shipped with a critical fix regarding a missing polyfill for `clearTimeout`, used by babel-polyfill. | ||
* 2016-06-06: 6.0.2 shipped with a critical fix if you are fragment caching the server generated React. | ||
* 2016-08-21: v6.1 ships with serveral new features and bug fixes. See the [Changelog](CHANGELOG.md). | ||
* 2016-07-28: If you're doing server rendering, be sure to use mini\_racer! See [issues/428](https://github.com/shakacode/react_on_rails/issues/428). It's supposedly much faster than `therubyracer`. | ||
* *See [NEWS.md](NEWS.md) for more notes over time.* | ||
@@ -21,44 +22,8 @@ | ||
**Project Objective**: To provide an opinionated and optimal framework for integrating **Ruby on Rails** with modern JavaScript tooling and libraries, including [**Webpack**](http://webpack.github.io/), [**Babel**](https://babeljs.io/), [**React**](https://facebook.github.io/react/), [**Redux**](https://github.com/reactjs/redux), [**React-Router**](https://github.com/reactjs/react-router). This differs significantly from typical Rails. When considering what goes into **react_on_rails**, we ask ourselves, is the functionality related to the intersection of using Rails and with modern JavaScript? If so, then the functionality belongs right here. In other cases, we're releasing separate npm packages or Ruby gems. If you are interested in implementing React using traditional Rails architecture, see [react-rails](https://github.com/reactjs/react-rails). | ||
**Project Objective**: To provide an opinionated and optimal framework for integrating Ruby on Rails with modern JavaScript tooling and libraries, including [**Webpack**](http://webpack.github.io/), [**Babel**](https://babeljs.io/), [**React**](https://facebook.github.io/react/), [**Redux**](https://github.com/reactjs/redux), [**React-Router**](https://github.com/reactjs/react-router). This differs significantly from typical Rails architecture. When considering what goes into **react_on_rails**, we ask ourselves, is the functionality related to the intersection of using Rails and modern JavaScript? If so, then the functionality belongs right here. In other cases, we're releasing separate npm packages or Ruby gems. If you are interested in implementing React using traditional Rails architecture, see [react-rails](https://github.com/reactjs/react-rails). | ||
React on Rails integrates Facebook's [React](https://github.com/facebook/react) front-end framework with Rails. React v0.14.x and greater is supported, with server rendering. [Redux](https://github.com/reactjs/redux) and [React-Router](https://github.com/reactjs/react-redux) are supported as well, also with server rendering, using either **execJS** or a [Node.js server](https://github.com/shakacode/react_on_rails/blob/master/docs%2Fadditional-reading%2Fnode-server-rendering.md). See the Rails on Maui [blog post](http://www.railsonmaui.com/blog/2014/10/03/integrating-webpack-and-the-es6-transpiler-into-an-existing-rails-project/) that started it all! | ||
## Including your React Component in your Rails Views | ||
Please see [Getting Started](#getting-started) for how to set up your Rails project for React on Rails to understand how `react_on_rails` can see your ReactComponents. | ||
## Table of Contents | ||
+ *Normal Mode (React component will be rendered on client):* | ||
```erb | ||
<%= react_component("HelloWorldApp", props: @some_props) %> | ||
``` | ||
+ *Server-Side Rendering (React component is first rendered into HTML on the server):* | ||
```erb | ||
<%= react_component("HelloWorldApp", props: @some_props, prerender: true) %> | ||
``` | ||
+ The `component_name` parameter is a string matching the name you used to globally expose your React component. So, in the above examples, if you had a React component named "HelloWorldApp," you would register it with the following lines: | ||
```js | ||
import ReactOnRails from 'react-on-rails'; | ||
import HelloWorldApp from './HelloWorldApp'; | ||
ReactOnRails.register({ HelloWorldApp }); | ||
``` | ||
Exposing your component in this way is how React on Rails is able to reference your component from a Rails view. You can expose as many components as you like, as long as their names do not collide. See below for the details of how you expose your components via the react_on_rails webpack configuration. | ||
+ `@some_props` can be either a hash or JSON string. This is an optional argument assuming you do not need to pass any options (if you want to pass options, such as `prerender: true`, but you do not want to pass any properties, simply pass an empty hash `{}`). This will make the data available in your component: | ||
```ruby | ||
# Rails View | ||
<%= react_component("HelloWorldApp", props: { name: "Stranger" }) %> | ||
``` | ||
```javascript | ||
// inside your React component | ||
this.props.name // "Stranger" | ||
``` | ||
## Documentation | ||
+ [Features](#features) | ||
@@ -69,2 +34,3 @@ + [Why Webpack?](#why-webpack) | ||
- [Initializer Configuration: config/initializers/react_on_rails.rb](#initializer-configuration) | ||
- [Including your React Component in your Rails Views](#including-your-react-component-in-your-rails-views) | ||
+ [How it Works](#how-it-works) | ||
@@ -80,3 +46,3 @@ - [Client-Side Rendering vs. Server-Side Rendering](#client-side-rendering-vs-server-side-rendering) | ||
+ [Integration with Node](#integration-with-node) | ||
+ [Additional Reading](#additional-reading) | ||
+ [Additional Documentation](#additional-documentation) | ||
+ [Contributing](#contributing) | ||
@@ -110,2 +76,4 @@ + [License](#license) | ||
## Getting Started | ||
**For more detailed instructions**, see the [React on Rails Basic Tutorial](docs/tutorial.md). | ||
1. Add the following to your Gemfile and bundle install. | ||
@@ -153,2 +121,37 @@ | ||
### Including your React Component in your Rails Views | ||
+ *Normal Mode (React component will be rendered on client):* | ||
```erb | ||
<%= react_component("HelloWorldApp", props: @some_props) %> | ||
``` | ||
+ *Server-Side Rendering (React component is first rendered into HTML on the server):* | ||
```erb | ||
<%= react_component("HelloWorldApp", props: @some_props, prerender: true) %> | ||
``` | ||
+ The `component_name` parameter is a string matching the name you used to expose your React component globally. So, in the above examples, if you had a React component named "HelloWorldApp," you would register it with the following lines: | ||
```js | ||
import ReactOnRails from 'react-on-rails'; | ||
import HelloWorldApp from './HelloWorldApp'; | ||
ReactOnRails.register({ HelloWorldApp }); | ||
``` | ||
Exposing your component in this way is how React on Rails is able to reference your component from a Rails view. You can expose as many components as you like, as long as their names do not collide. See below for the details of how you expose your components via the react_on_rails webpack configuration. | ||
+ `@some_props` can be either a hash or JSON string. This is an optional argument assuming you do not need to pass any options (if you want to pass options, such as `prerender: true`, but you do not want to pass any properties, simply pass an empty hash `{}`). This will make the data available in your component: | ||
```ruby | ||
# Rails View | ||
<%= react_component("HelloWorldApp", props: { name: "Stranger" }) %> | ||
``` | ||
```javascript | ||
// inside your React component | ||
this.props.name // "Stranger" | ||
``` | ||
## NPM | ||
@@ -169,5 +172,5 @@ All JavaScript in React On Rails is loaded from npm: [react-on-rails](https://www.npmjs.com/package/react-on-rails). To manually install this (you did not use the generator), assuming you have a standard configuration, run this command: | ||
### Client-Side Rendering vs. Server-Side Rendering | ||
In most cases, you should use the `prerender: false` (default behavior) with the provided helper method to render the React component from your Rails views. In some cases, such as when SEO is vital or many users will not have JavaScript enabled, you can enable server-rendering by passing `prerender: true` to your helper, or you can simply change the default in `config/initializers/react_on_rails`. | ||
In most cases, you should use the `prerender: false` (default behavior) with the provided helper method to render the React component from your Rails views. In some cases, such as when SEO is vital, or many users will not have JavaScript enabled, you can enable server-rendering by passing `prerender: true` to your helper, or you can simply change the default in `config/initializers/react_on_rails`. | ||
Now the server will interpret your JavaScript using [ExecJS](https://github.com/rails/execjs) and pass the resulting HTML to the client. We recommend using [therubyracer](https://github.com/cowboyd/therubyracer) as ExecJS's runtime. The generator will automatically add it to your Gemfile for you. | ||
Now the server will interpret your JavaScript using [ExecJS](https://github.com/rails/execjs) and pass the resulting HTML to the client. We recommend using [mini_racer](https://github.com/discourse/mini_racer) as ExecJS's runtime. The generator will automatically add it to your Gemfile for you (once we complete [#501](https://github.com/shakacode/react_on_rails/issues/501)). | ||
@@ -210,7 +213,7 @@ In the following screenshot you can see the 3 parts of React on Rails rendering: | ||
Note, you never make these calls. This is what React on Rails does when either server or client rendering. You'll be definining functions that take take these params and return a React component or a Redux Store. | ||
Note, you never make these calls. This is what React on Rails does when either server or client rendering. You'll be defining functions that take these params and return a React component or a Redux Store. | ||
(Note, see below [section](#multiple-react-components-on-a-page-with-one-store) on how to setup redux stores that allow multiple components to talk to the same store.) | ||
The `railsContext` has: (see implementation in file react_on_rails_helper.rb for method rails_context for the definitive list). | ||
The `railsContext` has: (see implementation in file [react_on_rails_helper.rb](app/helpers/react_on_rails_helper.rb), method `rails_context` for the definitive list). | ||
@@ -294,3 +297,3 @@ ```ruby | ||
You may want different initialization for your server rendered components. For example, if you have animation that runs when a component is displayed, you might need to turn that off when server rendering. However, the `railsContext` will tell you if your JavaScript code is running client side or server side. So code that required a different server bundle previously may no longer require this! | ||
If you do want different code to run, you'd setup a separate webpack compilation file and you'd specify a different, server side entry file. ex. 'serverHelloWorldApp.jsx'. Note, you might be initializing HelloWorld with version specialized for server rendering. | ||
@@ -320,3 +323,3 @@ | ||
+ **id:** Id for the div, will be used to attach the React component. This will get assigned automatically if you do not provide an id. Must be unique. | ||
+ **html_options:** Any other html options to get placed on the added div for the component. | ||
+ **html_options:** Any other html options to get placed on the added div for the component. For example, you can set a class (or inline style) on the outer div so that it behaves like a span, with styling of `display:inline-block`. | ||
+ **trace:** set to true to print additional debugging information in the browser. Defaults to true for development, off otherwise. Note, on the client you will so both the railsContext and your props. On the server, you only see the railsContext being logged. | ||
@@ -352,3 +355,3 @@ + **replay_console:** Default is true. False will disable echoing server-rendering logs to the browser. While this can make troubleshooting server rendering difficult, so long as you have the default configuration of logging_on_server set to true, you'll still see the errors on the server. | ||
1. You want to have multiple components that access the same store. | ||
2. You want to place the props to hydrate the client side stores at the very end of your HTML, so the browser can render all earlier HTML first. This is particularly useful if your props will be large. | ||
2. You want to place the props to hydrate the client side stores at the very end of your HTML so that the browser can render all earlier HTML first. This is particularly useful if your props will be large. | ||
@@ -362,3 +365,3 @@ ### Generator Functions | ||
+ js_expression, like 2 + 3, and not a block of js code. If you have more than one line that needs to be executed, wrap it in an [IIFE](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression). JS exceptions will be caught and console messages handled properly | ||
+ Currently the only option you may pass is `replay_console` (boolean) | ||
+ Currently, the only option you may pass is `replay_console` (boolean) | ||
@@ -368,5 +371,5 @@ This is a helper method that takes any JavaScript expression and returns the output from evaluating it. If you have more than one line that needs to be executed, wrap it in an IIFE. JS exceptions will be caught and console messages handled properly. | ||
## Multiple React Components on a Page with One Store | ||
You may wish to have 2 React components share the same the Redux store. For example, if your navbar is a React component, you may want it to use the same store as your component in the main area of the page. You may even want multiple React components in the main area, which allows for greater modularity. In addition, you may want this to work with Turbolinks to minimize reloading the JavaScript. A good example of this would be something like an a notifications counter in a header. As each notifications is read in the body of the page, you would like to update the header. If both the header and body share the same Redux store, then this is trivial. Otherwise, we have to rely on other solutions, such as the header polling the server to see how many unread notifications exist. | ||
You may wish to have 2 React components share the same the Redux store. For example, if your navbar is a React component, you may want it to use the same store as your component in the main area of the page. You may even want multiple React components in the main area, which allows for greater modularity. In addition, you may want this to work with Turbolinks to minimize reloading the JavaScript. A good example of this would be something like a notifications counter in a header. As each notification is read in the body of the page, you would like to update the header. If both the header and body share the same Redux store, then this is trivial. Otherwise, we have to rely on other solutions, such as the header polling the server to see how many unread notifications exist. | ||
Suppose the Redux store is called `appStore`, and you have 3 React components that each need to connect to a store: `NavbarApp`, `CommentsApp`, and `BlogsApp`. I named them with `App` to indicate that they are the registered components. | ||
Suppose the Redux store is called `appStore`, and you have 3 React components that each needs to connect to a store: `NavbarApp`, `CommentsApp`, and `BlogsApp`. I named them with `App` to indicate that they are the registered components. | ||
@@ -425,4 +428,20 @@ You will need to make a function that can create the store you will be using for all components and register it via the `registerStore` method. Note, this is a **storeCreator**, meaning that it is a function that takes (props, location) and returns a store: | ||
## ReactOnRails JavaScript API | ||
See [ReactOnRails JavaScriptAPI](docs/api/javascript-api.md). | ||
See [ReactOnRails JavaScript API](docs/api/javascript-api.md). | ||
#### Using Rails built-in CSRF protection in JavaScript | ||
Rails has built-in protection for Cross-Site Request Forgery (CSRF), see [Rails Documentation](http://guides.rubyonrails.org/security.html#cross-site-request-forgery-csrf). To nicely utilize this feature in JavaScript requests, React on Rails is offerring two helpers that can be used as following for POST, PULL or DELETE requests: | ||
``` | ||
import ReactOnRails from 'react-on-rails'; | ||
// reads from DOM csrf token generated by Rails in <%= csrf_meta_tags %> | ||
csrfToken = ReactOnRails.authenticityToken(); | ||
// compose Rails specific request header as following { X-CSRF-Token: csrfToken, X-Requested-With: XMLHttpRequest } | ||
header = ReactOnRails.authenticityHeaders(otherHeader); | ||
``` | ||
If you are using [jquery-ujs](https://github.com/rails/jquery-ujs) for AJAX calls, than these helpers are not needed because the [jquery-ujs](https://github.com/rails/jquery-ujs) library updates header automatically, see [jquery-ujs documentation](https://robots.thoughtbot.com/a-tour-of-rails-jquery-ujs#cross-site-request-forgery-protection). | ||
## React Router | ||
@@ -435,3 +454,5 @@ [React Router](https://github.com/reactjs/react-router) is supported, including server side rendering! See: | ||
## Deployment | ||
* Version 6.0 puts the necessary precompile steps automatically in the rake precompile step. You can, however, disable this by setting certain values to nil in the [config/react_on_rails.rb](config/react_on_rails.rb). | ||
* Version 6.0 puts the necessary precompile steps automatically in the rake precompile step. You can, however, disable this by setting certain values to nil in the [config/initializers/react_on_rails.rb](spec/dummy/config/initializers/react_on_rails.rb). | ||
* `config.symlink_non_digested_assets_regex`: Set to nil to turn off the setup of non-js assets. | ||
* `npm_build_production_command`: Set to nil to turn off the precompilation of the js assets. | ||
* See the [Heroku Deployment](docs/additional-reading/heroku-deployment.md) doc for specifics regarding Heroku. | ||
@@ -441,138 +462,52 @@ * If you're using the node server for server rendering, you may want to do your own AWS install. We'll have more docs on this in the future. | ||
## Integration with Node | ||
NodeJS can be used as the backend for server-side rendering instead of ExecJS. To do this you need to add a few files and then configure react_on_rails to use NodeJS. Here are the relevant files to add. | ||
Node.js can be used as the backend for server-side rendering instead of [execJS](https://github.com/rails/execjs). Before you try this, consider the tradeoff of extra complexity with your deployments versus *potential* performance gains. We've found that using ExecJS with [mini_racer](https://github.com/discourse/mini_racer) to be "fast enough" so far. That being said, we've heard of other large websites using Node.js for better server rendering performance. See [Node.js for Server Rendering](docs/additional-reading/node-server-rendering.md) for more information. | ||
```javascript | ||
// client/node/package.json | ||
{ | ||
"name": "react_on_rails_node", | ||
"version": "0.0.0", | ||
"private": true, | ||
"scripts": { | ||
"start": "node ./server.js -s webpack-bundle.js" | ||
}, | ||
"dependencies": { | ||
} | ||
} | ||
``` | ||
## Additional Documentation | ||
+ **Rails** | ||
+ [Rails Assets](docs/additional-reading/rails-assets.md) | ||
+ [Rails View Rendering from Inline JavaScript](docs/additional-reading/rails_view_rendering_from_inline_javascript.md) | ||
+ [RSpec Configuration](docs/additional-reading/rspec-configuration.md) | ||
+ [Turbolinks](docs/additional-reading/turbolinks.md) | ||
```javascript | ||
// client/node/server.js | ||
var net = require('net'); | ||
var fs = require('fs'); | ||
+ **Javascript** | ||
+ [Node Dependencies and NPM](docs/additional-reading/node-dependencies-and-npm.md) | ||
+ [Babel](docs/additional-reading/babel.md) | ||
+ [React Router](docs/additional-reading/react-router.md) | ||
+ [React & Redux](docs/additional-reading/react-and-redux.md) | ||
+ [Webpack](docs/additional-reading/webpack.md) | ||
+ [Webpack Configuration](docs/additional-reading/webpack.md) | ||
+ [Webpack Cookbook](https://christianalfoni.github.io/react-webpack-cookbook/index.html) | ||
+ [Developing with the Webpack Dev Server](docs/additional-reading/webpack-dev-server.md) | ||
+ [Node Server Rendering](docs/additional-reading/node-server-rendering.md) | ||
+ [Server Rendering Tips](docs/additional-reading/server-rendering-tips.md) | ||
var bundlePath = '../../app/assets/webpack/'; | ||
var bundleFileName = 'webpack-bundle.js'; | ||
+ **Development** | ||
+ [React on Rails Basic Installation Tutorial](docs/tutorial.md) ([live demo](https://hello-react-on-rails.herokuapp.com)) | ||
+ [Installation Overview](docs/basics/installation-overview.md) | ||
+ [Migration from react-rails](docs/basics/migrating-from-react-rails.md) | ||
+ [Recommended Project Structure](docs/additional-reading/recommended-project-structure.md) | ||
+ [Generator Tips](docs/basics/generator.md) | ||
+ [Hot Reloading of Assets For Rails Development](docs/additional-reading/hot-reloading-rails-development.md) | ||
+ [Heroku Deployment](docs/additional-reading/heroku-deployment.md) | ||
+ [Updating Dependencies](docs/additional-reading/updating-dependencies.md) | ||
var currentArg; | ||
+ **API** | ||
+ [JavaScript API](docs/api/javascript-api.md) | ||
+ [Ruby API](docs/api/ruby-api.md) | ||
+ [Setting up Hot Reloading during Rails Development, API docs](docs/api/ruby-api-hot-reload-view-helpers.md) | ||
function Handler() { | ||
this.queue = []; | ||
this.initialized = false; | ||
} | ||
+ **[CONTRIBUTING](CONTRIBUTING.MD)** | ||
+ [Generator Testing](docs/contributor-info/generator-testing.md) | ||
+ [Linting](docs/contributor-info/linters.md) | ||
+ [Releasing](docs/contributor-info/releasing.md) | ||
Handler.prototype.handle = function (connection) { | ||
var callback = function () { | ||
connection.setEncoding('utf8'); | ||
connection.on('data', (data)=> { | ||
console.log('Processing request: ' + data); | ||
var result = eval(data); | ||
connection.write(result); | ||
}); | ||
}; | ||
+ **Misc** | ||
+ [Tips](docs/additional-reading/tips.md) | ||
+ [Changelog](CHANGELOG.md) | ||
+ [Projects](PROJECTS.md) | ||
+ [Shaka Code Style](docs/coding-style/style.md) | ||
+ [React on Rails, Slides](http://www.slideshare.net/justingordon/react-on-rails-v4032) | ||
+ [Code of Conduct](docs/misc/code_of_conduct.md) | ||
+ [The React on Rails Doctrine](https://medium.com/@railsonmaui/the-react-on-rails-doctrine-3c59a778c724) | ||
if (this.initialized) { | ||
callback(); | ||
} else { | ||
this.queue.push(callback); | ||
} | ||
}; | ||
Handler.prototype.initialize = function () { | ||
console.log('Processing ' + this.queue.length + ' pending requests'); | ||
var callback; | ||
while (callback = this.queue.pop()) { | ||
callback(); | ||
} | ||
this.initialized = true; | ||
}; | ||
var handler = new Handler(); | ||
process.argv.forEach((val) => { | ||
if (val[0] == '-') { | ||
currentArg = val.slice(1); | ||
return; | ||
} | ||
if (currentArg == 's') { | ||
bundleFileName = val; | ||
} | ||
}); | ||
try { | ||
fs.mkdirSync(bundlePath); | ||
} catch (e) { | ||
if (e.code != 'EEXIST') throw e; | ||
} | ||
fs.watchFile(bundlePath + bundleFileName, (curr) => { | ||
if (curr && curr.blocks && curr.blocks > 0) { | ||
if (handler.initialized) { | ||
console.log('Reloading server bundle must be implemented by restarting the node process!'); | ||
return; | ||
} | ||
require(bundlePath + bundleFileName); | ||
console.log('Loaded server bundle: ' + bundlePath + bundleFileName); | ||
handler.initialize(); | ||
} | ||
}); | ||
var unixServer = net.createServer(function (connection) { | ||
handler.handle(connection); | ||
}); | ||
unixServer.listen('node.sock'); | ||
process.on('SIGINT', () => { | ||
unixServer.close(); | ||
process.exit(); | ||
}); | ||
``` | ||
The last thing you'll need to do is change the server_render_method to "NodeJS". | ||
```ruby | ||
# app/config/initializers/react_on_rails.rb | ||
config.server_render_method = "NodeJS" | ||
``` | ||
## Additional Reading | ||
+ [JavaScript API](docs/api/javascript-api.md) | ||
+ [Ruby API](docs/api/ruby-api.md) | ||
+ [Setting up Hot Reloading during Rails Development, API docs](docs/api/ruby-api-hot-reload-view-helpers.md) | ||
+ [React on Rails, Slides](http://www.slideshare.net/justingordon/react-on-rails-v4032) | ||
+ [The React on Rails Doctrine](https://medium.com/@railsonmaui/the-react-on-rails-doctrine-3c59a778c724) | ||
+ [Installation Overview](docs/basics/installation-overview.md) | ||
+ [Migration from react-rails](docs/basics/migrating-from-react-rails.md) | ||
+ [Babel](docs/additional-reading/babel.md) | ||
+ [Heroku Deployment](docs/additional-reading/heroku-deployment.md) | ||
+ [Manual Installation](docs/additional-reading/manual-installation.md) | ||
+ [Hot Reloading of Assets For Rails Development](docs/additional-reading/hot-reloading-rails-development.md) | ||
+ [Node Dependencies and NPM](docs/additional-reading/node-dependencies-and-npm.md) | ||
+ [React Router](docs/additional-reading/react-router.md) | ||
+ [RSpec Configuration](docs/additional-reading/rspec-configuration.md) | ||
+ [Server Rendering Tips](docs/additional-reading/server-rendering-tips.md) | ||
+ [Rails View Rendering from Inline JavaScript](docs/additional-reading/rails_view_rendering_from_inline_javascript.md) | ||
+ [Tips](docs/additional-reading/tips.md) | ||
+ [Tutorial for v2.0](docs/tutorial-v2.md), deployed [here](https://shakacode-react-on-rails.herokuapp.com/). | ||
+ [Turbolinks](docs/additional-reading/turbolinks.md) | ||
+ [Webpack Configuration](docs/additional-reading/webpack.md) | ||
+ [Webpack Cookbook](https://christianalfoni.github.io/react-webpack-cookbook/index.html) | ||
+ [Changelog](CHANGELOG.md) | ||
+ [Projects](PROJECTS.md) | ||
+ [Developing with the Webpack Dev Server](docs/additional-reading/webpack-dev-server) | ||
## Demos | ||
@@ -593,5 +528,5 @@ + [www.reactrails.com](http://www.reactrails.com) with the source at [shakacode/react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial/). | ||
## Contributing | ||
Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to our version of the [Contributor Covenant Code of Conduct](docs/code_of_conduct.md)). | ||
Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to our version of the [Contributor Covenant Code of Conduct](docs/misc/code_of_conduct.md)). | ||
See [Contributing](docs/contributor-info/contributing.md) to get started. | ||
See [Contributing](CONTRIBUTING.md) to get started. | ||
@@ -610,8 +545,14 @@ ## License | ||
## About [ShakaCode](http://www.shakacode.com/) | ||
# FINAL NOTES | ||
* See [Projects](PROJECTS.md) using and [KUDOS](./KUDOS.md) for React on Rails. Please submit yours! Please edit either page or [email us](mailto:contact@shakacode.com) and we'll add your info. We also **love stars** as it helps us attract new users and contributors. | ||
* Follow [@railsonmaui](https://twitter.com/railsonmaui) and [@shakacode](https://twitter.com/shakacode) on Twitter for updates on releases. We've also got a forum category dedicated to [react_on_rails](http://forum.shakacode.com/c/rails/ReactOnRails). | ||
Visit [our forums!](http://forum.shakacode.com). We've got a [category dedicated to react_on_rails](http://forum.shakacode.com/c/rails/reactonrails). | ||
--- | ||
If you're looking for consulting on a project using React and Rails, email us ([contact@shakacode.com](mailto: contact@shakacode.com))! You can also join our slack room for some free advice. | ||
Aloha from Justin Gordon ([bio](http://www.railsonmaui.com/about)) and the [ShakaCode](http://www.shakacode.com) Team! We're actively looking for new projects. If you like **React on Rails**, please consider contacting me at [justin@shakacode.com](mailto:justin@shakacode.com) if we could potentially help you in any way. Besides consulting on bigger projects, [ShakaCode](http://www.shakacode.com) is doing Skype plus Slack/Github based coaching for React on Rails. [Click here](http://www.shakacode.com/work/index.html) for more information. | ||
We're looking for great developers that want to work with Rails + React with a distributed, worldwide team, for our own products, client work, and open source. [More info here](http://www.shakacode.com/about/index.html#work-with-us). | ||
We're offering a free half-hour project consultation, on anything from React on Rails to any aspect of web application development for both consumer and enterprise products. In addition to React.js and Rails, we're doing react-native iOS and Android apps! | ||
Whether you have a new project or need help on an existing project, feel free to contact me directly at [justin@shakacode.com](mailto:justin@shakacode.com) and thanks in advance for any referrals! | ||
Your support keeps this project going. |
99893
17
748
28
542