react-rails 1.11.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +294 -214
  4. data/lib/assets/javascripts/react_ujs.js +429 -7
  5. data/lib/generators/react/component_generator.rb +24 -12
  6. data/lib/generators/react/install_generator.rb +76 -18
  7. data/lib/generators/templates/react_server_rendering.rb +2 -0
  8. data/lib/generators/templates/server_rendering.js +6 -0
  9. data/lib/generators/templates/server_rendering_pack.js +5 -0
  10. data/lib/react/jsx.rb +2 -0
  11. data/lib/react/rails/component_mount.rb +23 -5
  12. data/lib/react/rails/controller_lifecycle.rb +35 -7
  13. data/lib/react/rails/railtie.rb +17 -11
  14. data/lib/react/rails/version.rb +1 -1
  15. data/lib/react/server_rendering.rb +16 -4
  16. data/lib/react/server_rendering/{sprockets_renderer.rb → bundle_renderer.rb} +40 -20
  17. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_polyfill.js +0 -0
  18. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_replay.js +1 -1
  19. data/lib/react/server_rendering/bundle_renderer/console_reset.js +3 -0
  20. data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/timeout_polyfill.js +0 -0
  21. data/lib/react/server_rendering/exec_js_renderer.rb +4 -1
  22. data/lib/react/server_rendering/webpacker_manifest_container.rb +34 -0
  23. data/lib/react/server_rendering/yaml_manifest_container.rb +1 -1
  24. metadata +16 -16
  25. data/lib/assets/javascripts/react_ujs_event_setup.js +0 -29
  26. data/lib/assets/javascripts/react_ujs_mount.js +0 -104
  27. data/lib/assets/javascripts/react_ujs_native.js +0 -18
  28. data/lib/assets/javascripts/react_ujs_pjax.js +0 -10
  29. data/lib/assets/javascripts/react_ujs_turbolinks.js +0 -9
  30. data/lib/assets/javascripts/react_ujs_turbolinks_classic.js +0 -10
  31. data/lib/assets/javascripts/react_ujs_turbolinks_classic_deprecated.js +0 -13
  32. data/lib/generators/react/ujs_generator.rb +0 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5be5071bf0476f2792eff912d657e0bf93ead10e
4
- data.tar.gz: 2c77127d9a17d3ba653086034c61a087714f0476
3
+ metadata.gz: e9fe44131627108a965e3ab9eb09d5dfd1880926
4
+ data.tar.gz: aabdd492d7f9dd5cf9e17d7f10ef8547749cb8ca
5
5
  SHA512:
6
- metadata.gz: 8fab516013ce7f24199968b2ed3f6183998921fa97632afb2ea4beff96fcb4bf85a8c8371d383cb60d0a866501dc770f39d6a8ae00906603a11d5520a4d1f521
7
- data.tar.gz: 79d8ca952f890e00e8116939ba4136229c470c34ba5424bf9ca7eae052c3dbf67c7646fa3e3fdc4126e29241de25abc257ffd01c93375c293098bdd374012107
6
+ metadata.gz: 0e13b6d9112c96ca74ecc0f2fc78540fc6577a63f7e46ae2e5cb9fa481726d5f879d8f63a2e5f479c572398bea14c3b37bed4c45952175e287d6dfd02602a0f8
7
+ data.tar.gz: d8c03917cabcf1d59b399fb10162642f5e76ea31b8d4f08ef5d223a3e2c713250c536a421db8f44d480ffae16d20f15b79b015bc5950a3977ff8796aea194805
@@ -8,6 +8,35 @@
8
8
 
9
9
  #### Bug Fixes
10
10
 
11
+ ## 2.0.0 (April 13, 2017)
12
+
13
+ #### Breaking Changes
14
+
15
+ - Server rendering loads `server_rendering.js` by default #471 . Upgrade by adding a new file which requires the previous defaults:
16
+
17
+ ```js
18
+ // app/assets/javascripts/server_rendering.js
19
+ // = require react-server
20
+ // = require components
21
+ ```
22
+
23
+
24
+ #### New Features
25
+
26
+ - Webpacker support:
27
+ - `react_component` can find components via `require.context` + `ReactRailsUJS.useContext` #678
28
+ - Server rendering detects Webpacker and uses packs #683, #687
29
+ - `ReactRailsUJS` is available from `npm` with `yarn add react_ujs` or `npm install react_ujs` #678
30
+ - `per_request_react_rails_prerenderer` Allows you to check out a renderer for the _whole request_ instead of once-per-`react_component` #559
31
+
32
+ #### Bug Fixes
33
+
34
+ - Improved watching of server-rendering JS files #687
35
+ - Fix console replay:
36
+ - Put the `<script>` tag outside the React.js container to avoid React warnings #691
37
+ - Clear console history between renders #692
38
+ - Use better Turbolinks events #690
39
+
11
40
  ## 1.11.0 (April 4, 2017)
12
41
 
13
42
  #### New Features
data/README.md CHANGED
@@ -1,116 +1,176 @@
1
+ # react-rails
2
+
1
3
  [![Gem](https://img.shields.io/gem/v/react-rails.svg?style=flat-square)](http://rubygems.org/gems/react-rails)
2
4
  [![Build Status](https://img.shields.io/travis/reactjs/react-rails/master.svg?style=flat-square)](https://travis-ci.org/reactjs/react-rails)
3
5
  [![Gemnasium](https://img.shields.io/gemnasium/reactjs/react-rails.svg?style=flat-square)](https://gemnasium.com/reactjs/react-rails)
4
6
  [![Code Climate](https://img.shields.io/codeclimate/github/reactjs/react-rails.svg?style=flat-square)](https://codeclimate.com/github/reactjs/react-rails)
5
7
  [![Test Coverage](https://img.shields.io/codeclimate/coverage/github/reactjs/react-rails.svg?style=flat-square)](https://codeclimate.com/github/reactjs/react-rails/coverage)
6
8
 
7
- * * *
9
+ `react-rails` makes it easy to use [React](http://facebook.github.io/react/) and [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) in your Ruby on Rails (3.2+) application. Learn more:
8
10
 
9
- # react-rails
11
+ - React's [Getting Started guide](https://facebook.github.io/react/docs/getting-started.html)
12
+ - Use React & JSX [with webpacker](#use-with-webpacker) or [with the asset pipeline](#use-with-asset-pipeline)
13
+ - Rendering [components in views](#view-helper) or [in controller actions](#controller-actions)
14
+ - [Server-side rendering](#server-side-rendering)
15
+ - [Generating components](#component-generator) in various formats
16
+ - [`ReactRailsUJS`](#ujs) for mounting and unmounting components
17
+ - Automatically [camelizing props](#camelize-props)
18
+ - [Related Projects](#related-projects)
19
+ - [Developing](#development) the gem
10
20
 
21
+ ## Installation
11
22
 
12
- `react-rails` makes it easy to use [React](http://facebook.github.io/react/) and [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html)
13
- in your Ruby on Rails (3.2+) application. `react-rails` can:
23
+ Install from Rubygems as `react-rails`.
14
24
 
15
- - Provide [various `react` builds](#reactjs-builds) to your asset bundle
16
- - Transform [`.jsx` in the asset pipeline](#jsx)
17
- - [Render components into views and mount them](#rendering--mounting) via view helper & `react_ujs`
18
- - [Render components server-side](#server-rendering) with `prerender: true`
19
- - [Generate components](#component-generator) with a Rails generator
20
- - [Be extended](#extending-react-rails) with custom renderers, transformers and view helpers
25
+ ```ruby
26
+ gem "react-rails"
27
+ ```
21
28
 
22
- Just getting started with React? Make sure to check out the [Getting Started][React Getting Started] guide. Also, see [Related Projects](#related-projects) below.
29
+ Get started with `rails g react:install`:
23
30
 
24
- ## Installation
31
+ ```
32
+ $ rails g react:install
33
+ ```
25
34
 
26
- Add `react-rails` to your gemfile:
35
+ ## Use with Webpacker
27
36
 
28
- ```ruby
29
- gem 'react-rails'
37
+ [webpacker](https://github.com/rails/webpacker) integrates modern JS tooling with Rails. `ReactRailsUJS` allows you to gradually migrate to webpacker.
38
+
39
+ Get started by adding `webpacker` to your gemfile and installing `webpacker` and `react-rails`:
40
+
41
+ ```
42
+ $ rails webpacker:install
43
+ $ rails webpacker:install:react
44
+ $ rails generate react:install
30
45
  ```
31
46
 
32
- And install:
47
+ This gives you:
48
+
49
+ - `components/` directory for your React components
50
+ - [`ReactRailsUJS`](#ujs) setup in `packs/application.js`
51
+ - `packs/server_rendering.js` for [server-side rendering](#server-side-rendering)
33
52
 
53
+ When you add a component to `components/`, you can [render it in a Rails view](#view-helper):
54
+
55
+ ```erb
56
+ <%= react_component("HelloWorld", { greeting: "Hello" }) %>
34
57
  ```
35
- bundle install
58
+
59
+ The component name tells `react-rails` where to load the component. For example:
60
+
61
+ `react_component` call | component `require`
62
+ =======================|======================
63
+ `react_component("Item")` | `require("Item")`
64
+ `react_component("items/index")` | `require("items/index")`
65
+ `react_component("items.Index")` | `require("items").Index`
66
+ `react_component("items.Index.Header")` | `require("items").Index.Header`
67
+
68
+ This way, you can access top-level, default, or named exports.
69
+
70
+ If `require` fails, `react-rails` falls back to the global namespace approach described in [Use with Asset Pipeline](#use-with-asset-pipeline).
71
+
72
+ The `require.context` inserted into `packs/application.js` is used to load components. If you want to load components from a different directory, override it by calling `ReactRailsUJS.useContext`:
73
+
74
+ ```js
75
+ var myCustomContext = require.context("custom_components", true)
76
+ var ReactRailsUJS = require("react_ujs")
77
+ // use `custom_components/` for <%= react_component(...) %> calls
78
+ ReactRailsUJS.useContext(myCustomContext)
36
79
  ```
37
80
 
38
- Next, run the installation script:
81
+ ## Use with Asset Pipeline
39
82
 
40
- ```bash
41
- rails g react:install
83
+ `react-rails` provides React.js & a UJS driver to the Rails asset pipeline. Get started by installing:
84
+
85
+ ```
86
+ $ rails g react:install
42
87
  ```
43
88
 
89
+ Then restart your development server.
90
+
44
91
  This will:
45
- - create a `components.js` manifest file and a `app/assets/javascripts/components/` directory,
46
- where you will put your components
47
- - place the following in your `application.js`:
48
92
 
49
- ```js
50
- //= require react
51
- //= require react_ujs
52
- //= require components
53
- ```
93
+ - add some `//= require`s to `application.js`
94
+ - add a `components/` directory for React components
95
+ - add `server_rendering.js` for [server-side rendering](#server-side-rendering)
54
96
 
55
- ## Usage
97
+ Now, you can create React components in `.jsx` files:
56
98
 
57
- ### React.js builds
99
+ ```js
100
+ // app/assets/javascripts/components/post.jsx
58
101
 
59
- You can pick which React.js build (development, production, with or without [add-ons]((http://facebook.github.io/react/docs/addons.html)))
60
- to serve in each environment by adding a config. Here are the defaults:
102
+ window.Post = React.createClass({
103
+ render: function() {
104
+ return <h1>{this.props.title}</h1>
105
+ }
106
+ })
61
107
 
62
- ```ruby
63
- # config/environments/development.rb
64
- MyApp::Application.configure do
65
- config.react.variant = :development
66
- end
108
+ // or, equivalent:
109
+ class Post extends React.Component {
110
+ render() {
111
+ return <h1>{this.props.title}</h1>
112
+ }
113
+ }
114
+ ```
67
115
 
68
- # config/environments/production.rb
69
- MyApp::Application.configure do
70
- config.react.variant = :production
71
- end
116
+ Then, you can render those [components in views](#view-helper):
117
+
118
+ ```erb
119
+ <%= react_component("Post", {title: "Hello World"}) %>
72
120
  ```
73
121
 
74
- To include add-ons, use this config:
122
+ Components must be accessible from the top level, but they may be namespaced, for example:
75
123
 
76
- ```ruby
77
- MyApp::Application.configure do
78
- config.react.addons = true # defaults to false
79
- end
124
+ ```erb
125
+ <%= react_component("Comments.NewForm", {post_id: @post.id}) %>
126
+ <!-- looks for `window.Comments.NewForm` -->
80
127
  ```
81
128
 
82
- After restarting your Rails server, `//= require react` will provide the build of React.js which
83
- was specified by the configurations.
129
+ ### Custom JSX Transformer
130
+
131
+ `react-rails` uses a transformer class to transform JSX in the asset pipeline. The transformer is initialized once, at boot. You can provide a custom transformer to `config.react.jsx_transformer_class`. The transformer must implement:
84
132
 
85
- `react-rails` offers a few other options for versions & builds of React.js.
86
- See [VERSIONS.md](https://github.com/reactjs/react-rails/blob/master/VERSIONS.md) for more info about
87
- using the `react-source` gem or dropping in your own copies of React.js.
133
+ - `#initialize(options)`, where options is the value passed to `config.react.jsx_transform_options`
134
+ - `#transform(code_string)` to return a string of transformed code
88
135
 
89
- ### JSX
136
+ `react-rails` provides two transformers, `React::JSX::BabelTransformer` (which uses [ruby-babel-transpiler](https://github.com/babel/ruby-babel-transpiler)) and `React::JSX::JSXTransformer` (which uses the deprecated `JSXTransformer.js`).
90
137
 
91
- After installing `react-rails`, restart your server. Now, `.js.jsx` files will be transformed in the asset pipeline.
138
+ ### React.js versions
92
139
 
93
- #### BabelTransformer options
140
+ `//= require react` brings `React` into your project.
94
141
 
95
- You can use babel's [transformers](http://henryzoo.com/babel.github.io/docs/advanced/transformers/) and [custom plugins](http://henryzoo.com/babel.github.io/docs/advanced/plugins/),
96
- and pass [options](http://babeljs.io/docs/usage/options/) to the babel transpiler adding following configurations:
142
+ To include `React.addons`, add this config:
97
143
 
98
144
  ```ruby
99
- config.react.jsx_transform_options = {
100
- blacklist: ['spec.functionName', 'validation.react', 'strict'], # default options
101
- optional: ["transformerName"], # pass extra babel options
102
- whitelist: ["useStrict"] # even more options
103
- }
145
+ # config/application.rb
146
+ MyApp::Application.configure do
147
+ config.react.addons = true # defaults to false
148
+ end
149
+ ```
150
+
151
+ By default, React's [development version] is provided to `Rails.env.development`. You can override the React build with a config:
152
+
153
+ ```ruby
154
+ # Here are the defaults:
155
+ # config/environments/development.rb
156
+ MyApp::Application.configure do
157
+ config.react.variant = :development
158
+ end
159
+
160
+ # config/environments/production.rb
161
+ MyApp::Application.configure do
162
+ config.react.variant = :production
163
+ end
104
164
  ```
105
- Under the hood, `react-rails` uses [ruby-babel-transpiler](https://github.com/babel/ruby-babel-transpiler), for transformation.
106
165
 
107
- ### Rendering & mounting
166
+ Be sure to restart your Rails server after changing these files. See [VERSIONS.md](https://github.com/reactjs/react-rails/blob/master/VERSIONS.md) to learn which version of React.js is included with your `react-rails` version.
167
+
168
+
169
+ ## View Helper
108
170
 
109
- `react-rails` includes a view helper (`react_component`) and an unobtrusive JavaScript driver (`react_ujs`)
110
- which work together to put React components on the page. You should require the UJS driver
111
- in your manifest after `react` (and after `turbolinks` if you use [Turbolinks](https://github.com/rails/turbolinks)).
171
+ `react-rails` includes a view helper and an [unobtrusive JavaScript driver](#ujs) which work together to put React components on the page.
112
172
 
113
- The __view helper__ puts a `div` on the page with the requested component class & props. For example:
173
+ The view helper (`react_component`) puts a `div` on the page with the requested component class & props. For example:
114
174
 
115
175
  ```erb
116
176
  <%= react_component('HelloMessage', name: 'John') %>
@@ -118,35 +178,87 @@ The __view helper__ puts a `div` on the page with the requested component class
118
178
  <div data-react-class="HelloMessage" data-react-props="{&quot;name&quot;:&quot;John&quot;}"></div>
119
179
  ```
120
180
 
121
- On page load, the __`react_ujs` driver__ will scan the page and mount components using `data-react-class`
181
+ On page load, the [`react_ujs` driver](#ujs) will scan the page and mount components using `data-react-class`
122
182
  and `data-react-props`.
123
183
 
124
- If Turbolinks is present components are mounted on the `page:change` event, and unmounted on `page:before-unload`.
125
- __Turbolinks >= 2.4.0__ is recommended because it exposes better events.
126
-
127
- In case of __Ajax calls__, the UJS mounting can be triggered manually by calling from javascript:
128
-
129
- ```javascript
130
- ReactRailsUJS.mountComponents()
131
- ```
132
-
133
184
  The view helper's signature is:
134
185
 
135
186
  ```ruby
136
187
  react_component(component_class_name, props={}, html_options={})
137
188
  ```
138
189
 
139
- - `component_class_name` is a string which names a globally-accessible component class. It may have dots (eg, `"MyApp.Header.MenuItem"`).
140
- - `props` is either an object that responds to `#to_json` or an already-stringified JSON object (eg, made with Jbuilder, see note below).
190
+ - `component_class_name` is a string which identifies a component. See [getConstructor](#getconstructor) for details.
191
+ - `props` is either:
192
+ - an object that responds to `#to_json`; or
193
+ - an already-stringified JSON object (see [JBuilder note](#use-with-jbuilder) below).
141
194
  - `html_options` may include:
142
195
  - `tag:` to use an element other than a `div` to embed `data-react-class` and `data-react-props`.
143
196
  - `prerender: true` to render the component on the server.
197
+ - `camelize_props` to [transform a props hash](#camelize_props)
144
198
  - `**other` Any other arguments (eg `class:`, `id:`) are passed through to [`content_tag`](http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag).
145
199
 
146
200
 
147
- ### Server rendering
201
+ #### Custom View Helper
202
+
203
+ `react-rails` uses a "helper implementation" class to generate the output of the `react_component` helper. The helper is initialized once per request and used for each `react_component` call during that request. You can provide a custom helper class to `config.react.view_helper_implementation`. The class must implement:
204
+
205
+ - `#react_component(name, props = {}, options = {}, &block)` to return a string to inject into the Rails view
206
+ - `#setup(controller_instance)`, called when the helper is initialized at the start of the request
207
+ - `#teardown(controller_instance)`, called at the end of the request
208
+
209
+ `react-rails` provides one implementation, `React::Rails::ComponentMount`.
210
+
211
+ ## UJS
212
+
213
+ `react-rails`'s JavaScript is available as `"react_ujs"` in the asset pipeline or from NPM. It attaches itself to the window as `ReactRailsUJS`.
214
+
215
+ ### Mounting & Unmounting
216
+
217
+ Usually, `react-rails` mounts & unmounts components automatically as described in [Event Handling](#event-handling) below.
218
+
219
+ You can also mount & unmount components from `<%= react_component(...) %>` tags using UJS:
220
+
221
+ ```js
222
+ // Mount all components on the page:
223
+ ReactRailsUJS.mountComponents()
224
+ // Mount components within a selector:
225
+ ReactRailsUJS.mountComponents(".my-class")
226
+ // Mount components within a specific node:
227
+ ReactRailsUJS.mountComponents(specificDOMnode)
228
+
229
+ // Unmounting works the same way:
230
+ ReactRailsUJS.unmountComponents()
231
+ ReactRailsUJS.unmountComponents(".my-class")
232
+ ReactRailsUJS.unmountComponents(specificDOMnode)
233
+ ```
234
+
235
+ You can use this when the DOM is modified by AJAX calls or modal windows.
236
+
237
+ ### Event Handling
238
+
239
+ `ReactRailsUJS` checks for various libraries to support their page change events:
240
+
241
+ - `Turbolinks`
242
+ - `pjax`
243
+ - `jQuery`
244
+ - Native DOM events
245
+
246
+ `ReactRailsUJS` will automatically mount components on `<%= react_component(...) %>` tags and unmount them when appropriate.
247
+
248
+ Be sure to load `react_ujs` _after_ these libraries so that it can detect them.
249
+
250
+ ### `getConstructor`
251
+
252
+ Components are loaded with `ReactRailsUJS.getConstructor(className)`. This function has two built-in implementations:
253
+
254
+ - On the asset pipeline, it looks up `className` in the global namespace.
255
+ - On webpacker, it `require`s files and accesses named exports, as described in [Use with Webpacker](#use-with-webpacker).
148
256
 
149
- To render components on the server, pass `prerender: true` to `react_component`:
257
+ You can override this function to customize the mapping of name-to-constructor. [Server-side rendering](#server-side-rendering) also uses this function.
258
+
259
+ ## Server-Side Rendering
260
+
261
+ You can render React components inside your Rails server with `prerender: true`:
150
262
 
151
263
  ```erb
152
264
  <%= react_component('HelloMessage', {name: 'John'}, {prerender: true}) %>
@@ -156,25 +268,22 @@ To render components on the server, pass `prerender: true` to `react_component`:
156
268
  </div>
157
269
  ```
158
270
 
159
- _(It will also be mounted by the UJS on page load.)_
271
+ _(It will also be mounted by the [UJS](#ujs) on page load.)_
160
272
 
161
- There are some requirements for this to work:
273
+ Server rendering is powered by [`ExecJS`](https://github.com/rails/execjs) and subject to some requirements:
162
274
 
163
- - `react-rails` must load your code. By convention, it uses `components.js`, which was created
275
+ - `react-rails` must load your code. By convention, it uses `server_rendering.js`, which was created
164
276
  by the install task. This file must include your components _and_ their dependencies (eg, Underscore.js).
165
- - Your components must be accessible in the global scope.
166
- If you are using `.js.jsx.coffee` files then the wrapper function needs to be taken into account:
167
-
168
- ```coffee
169
- # @ is `window`:
170
- @Component = React.createClass
171
- render: ->
172
- `<ExampleComponent videos={this.props.videos} />`
173
- ```
174
- - Your code can't reference `document`. Prerender processes don't have access to `document`,
277
+ - Your code can't reference `document` or `window`. Prerender processes don't have access to `document` or `window`,
175
278
  so jQuery and some other libs won't work in this environment :(
176
279
 
177
- You can configure your pool of JS virtual machines and specify where it should load code:
280
+ `ExecJS` supports many backends. CRuby users will get the best performance from [`mini_racer`](https://github.com/discourse/mini_racer#performance).
281
+
282
+ #### Configuration
283
+
284
+ Server renderers are stored in a pool and reused between requests. Threaded Rubies (eg jRuby) may see a benefit to increasing the pool size beyond the default `0`.
285
+
286
+ These are the default configurations:
178
287
 
179
288
  ```ruby
180
289
  # config/environments/application.rb
@@ -183,35 +292,71 @@ MyApp::Application.configure do
183
292
  # Settings for the pool of renderers:
184
293
  config.react.server_renderer_pool_size ||= 1 # ExecJS doesn't allow more than one on MRI
185
294
  config.react.server_renderer_timeout ||= 20 # seconds
186
- config.react.server_renderer = React::ServerRendering::SprocketsRenderer
295
+ config.react.server_renderer = React::ServerRendering::BundleRenderer
187
296
  config.react.server_renderer_options = {
188
- files: ["react-server.js", "components.js"], # files to load for prerendering
297
+ files: ["server_rendering.js"], # files to load for prerendering
189
298
  replay_console: true, # if true, console.* will be replayed client-side
190
299
  }
300
+ # Changing files matching these dirs/exts will cause the server renderer to reload:
301
+ config.react.server_renderer_extensions = ["jsx", "js"]
302
+ config.react.server_renderer_directories = ["/app/assets/javascripts", "/app/javascripts/"]
191
303
  end
192
304
  ```
193
305
 
194
- - On MRI, use `therubyracer` for the best performance (see [discussion](https://github.com/reactjs/react-rails/pull/290))
195
- - On MRI, you'll get a deadlock with `pool_size` > 1
196
- - If you're using JRuby, you can increase `pool_size` to have real multi-threaded rendering.
306
+ #### JavaScript State
307
+
308
+ Some of ExecJS's backends are stateful (eg, mini_racer, therubyracer). This means that any side-effects of a prerender will affect later renders with that renderer.
197
309
 
198
- You can configure camelize_props option and pass props with an underscored hash from rails but get a camelized hash in jsx :
310
+ To manage state, you have a couple options:
311
+
312
+ - Make a custom renderer with `#before_render` / `#after_render` hooks as [described below](#custom-server-renderer)
313
+ - Use `per_request_react_rails_prerenderer` to manage state for a whole controller action.
314
+
315
+ To check out a renderer for the duration of a controller action, call the `per_request_react_rails_prerenderer` helper in the controller class:
199
316
 
200
317
  ```ruby
201
- MyApp::Application.configure do
202
- config.react.camelize_props = true #default false
318
+ class PagesController < ApplicationController
319
+ # Use the same React server renderer for the entire request:
320
+ per_request_react_rails_prerenderer
203
321
  end
204
322
  ```
205
323
 
206
- or when mounting:
324
+ Then, you can access the ExecJS context directly with `react_rails_prerenderer.context`:
207
325
 
208
- ```erb
209
- <%= react_component('HelloMessage', {name: 'John'}, {camelize_props: true}) %>
326
+ ```ruby
327
+ def show
328
+ react_rails_prerenderer # => #<React::ServerRendering::BundleRenderer>
329
+ react_rails_prerenderer.context # => #<ExecJS::Context>
330
+
331
+ # Execute arbitrary JavaScript code
332
+ # `self` is the global context
333
+ react_rails_prerenderer.context.exec("self.Store.setup()")
334
+ render :show
335
+ react_rails_prerenderer.context.exec("self.Store.teardown()")
336
+ end
210
337
  ```
211
338
 
212
- ### Rendering components instead of views
339
+ `react_rails_prerenderer` may also be accessed in before- or after-actions.
340
+
341
+ #### Custom Server Renderer
342
+
343
+ `react-rails` depends on a renderer class for rendering components on the server. You can provide a custom renderer class to `config.react.server_renderer`. The class must implement:
344
+
345
+ - `#initialize(options={})`, which accepts the hash from `config.react.server_renderer_options`
346
+ - `#render(component_name, props, prerender_options)` to return a string of HTML
347
+
348
+ `react-rails` provides two renderer classes: `React::ServerRendering::ExecJSRenderer` and `React::ServerRendering::BundleRenderer`.
349
+
350
+ `ExecJSRenderer` offers two other points for extension:
351
+
352
+ - `#before_render(component_name, props, prerender_options)` to return a string of JavaScript to execute _before_ calling `React.render`
353
+ - `#after_render(component_name, props, prerender_options)` to return a string of JavaScript to execute _after_ calling `React.render`
354
+
355
+ Any subclass of `ExecJSRenderer` may use those hooks (for example, `BundleRenderer` uses them to handle `console.*` on the server).
356
+
357
+ ## Controller Actions
213
358
 
214
- Components can also be prerendered directly from a controller action with the custom `component` renderer. For example:
359
+ Components can also be server-rendered directly from a controller action with the custom `component` renderer. For example:
215
360
 
216
361
  ```ruby
217
362
  class TodoController < ApplicationController
@@ -222,31 +367,30 @@ class TodoController < ApplicationController
222
367
  end
223
368
  ```
224
369
 
225
- This custom renderer behaves the same as a normal view renderer and accepts the usual arguments - `content_type`, `layout`, `location` and `status`.
226
- By default, your current layout will be used and the component, rather than a view, will be rendered in place of `yield`. Custom data-* attributes
227
- can be passed like `data: {remote: true}`. Prerendering is set to `true` by default, but can be turned off like any other option: `prerender: false`.
370
+ You can also provide the "usual" `render` arguments: `content_type`, `layout`, `location` and `status`. By default, your current layout will be used and the component, rather than a view, will be rendered in place of `yield`. Custom data-* attributes can be passed like `data: {remote: true}`.
228
371
 
229
- ### Component generator
372
+ Prerendering is set to `true` by default, but can be turned off with `prerender: false`.
230
373
 
231
- `react-rails` ships with a Rails generator to help you get started with a simple component scaffold.
232
- You can run it using `rails generate react:component ComponentName (--es6)`.
233
- The generator takes an optional list of arguments for default propTypes,
234
- which follow the conventions set in the [Reusable Components](http://facebook.github.io/react/docs/reusable-components.html)
235
- section of the React documentation.
374
+ ## Component Generator
236
375
 
237
- For example:
376
+ You can generate a new component file with:
238
377
 
239
- ```shell
240
- rails generate react:component Post title:string body:string published:bool published_by:instanceOf{Person}
378
+ ```sh
379
+ rails g react:component ComponentName prop1:type prop2:type ...
241
380
  ```
242
381
 
243
- would generate the following in `app/assets/javascripts/components/post.js.jsx`:
382
+ For example,
244
383
 
245
- ```jsx
384
+ ```sh
385
+ rails g react:component Post title:string published:bool published_by:instanceOf{Person}
386
+ ```
387
+
388
+ would generate:
389
+
390
+ ```js
246
391
  var Post = React.createClass({
247
392
  propTypes: {
248
393
  title: React.PropTypes.string,
249
- body: React.PropTypes.string,
250
394
  published: React.PropTypes.bool,
251
395
  publishedBy: React.PropTypes.instanceOf(Person)
252
396
  },
@@ -255,7 +399,6 @@ var Post = React.createClass({
255
399
  return (
256
400
  <div>
257
401
  <div>Title: {this.props.title}</div>
258
- <div>Body: {this.props.body}</div>
259
402
  <div>Published: {this.props.published}</div>
260
403
  <div>Published By: {this.props.publishedBy}</div>
261
404
  </div>
@@ -264,49 +407,22 @@ var Post = React.createClass({
264
407
  });
265
408
  ```
266
409
 
267
- #### Options
268
-
269
- **--es6** : Generate the same component but using cutting edge es6 class
270
-
271
- For example:
272
-
273
- ```shell
274
- rails generate react:component Label label:string --es6
275
- ```
276
-
277
- **--coffee** : Generate the component using CoffeeScript syntax
278
-
279
- For example:
280
-
281
- ```shell
282
- rails generate react:component Label label:string --coffee
283
- ```
284
-
285
- #### Arguments
410
+ The generator also accepts options:
286
411
 
287
- The generator can use the following arguments to create basic propTypes:
412
+ - `--es6`: use `class ComponentName extends React.Component`
413
+ - `--coffee`: use CoffeeScript
288
414
 
289
- * any
290
- * array
291
- * bool
292
- * element
293
- * func
294
- * number
295
- * object
296
- * node
297
- * shape
298
- * string
415
+ Accepted PropTypes are:
299
416
 
300
- The following additional arguments have special behavior:
301
-
302
- * `instanceOf` takes an optional class name in the form of {className}.
303
- * `oneOf` behaves like an enum, and takes an optional list of strings in the form of `'name:oneOf{one,two,three}'`.
304
- * `oneOfType` takes an optional list of react and custom types in the form of `'model:oneOfType{string,number,OtherType}'`.
417
+ - Plain types: `any`, `array`, `bool`, `element`, `func`, `number`, `object`, `node`, `shape`, `string`
418
+ - `instanceOf` takes an optional class name in the form of `instanceOf{className}`.
419
+ - `oneOf` behaves like an enum, and takes an optional list of strings in the form of `'name:oneOf{one,two,three}'`.
420
+ - `oneOfType` takes an optional list of react and custom types in the form of `'model:oneOfType{string,number,OtherType}'`.
305
421
 
306
422
  Note that the arguments for `oneOf` and `oneOfType` must be enclosed in single quotes
307
423
  to prevent your terminal from expanding them into an argument list.
308
424
 
309
- ### Jbuilder & react-rails
425
+ #### Use with JBuilder
310
426
 
311
427
  If you use Jbuilder to pass a JSON string to `react_component`, make sure your JSON is a stringified hash,
312
428
  not an array. This is not the Rails default -- you should add the root node yourself. For example:
@@ -325,68 +441,33 @@ json.messages(@messages) do |message|
325
441
  end
326
442
  ```
327
443
 
328
- ## CoffeeScript
444
+ ### Camelize Props
329
445
 
330
- It is possible to use JSX with CoffeeScript. To use CoffeeScript, create files with an extension `.js.jsx.coffee`.
331
- We also need to embed JSX code inside backticks so that CoffeeScript ignores the syntax it doesn't understand.
332
- Here's an example:
446
+ You can configure `camelize_props` option:
333
447
 
334
- ```coffee
335
- Component = React.createClass
336
- render: ->
337
- `<ExampleComponent videos={this.props.videos} />`
448
+ ```ruby
449
+ MyApp::Application.configure do
450
+ config.react.camelize_props = true # default false
451
+ end
338
452
  ```
339
453
 
340
- Alternatively, the newer ES6 style class based syntax can be used like this:
454
+ Now, Ruby hashes given to `react_component(...)` as props will have their keys transformed from _underscore_- to _camel_-case, for example:
341
455
 
342
- ```coffee
343
- class Component extends React.Component
344
- render: ->
345
- `<ExampleComponent videos={this.props.videos} />`
456
+ ```ruby
457
+ { all_todos: @todos, current_status: @status }
458
+ # becomes:
459
+ { "allTodos" => @todos, "currentStatus" => @status }
346
460
  ```
347
461
 
348
- ## Extending `react-rails`
349
-
350
- You can extend some of the core functionality of `react-rails` by injecting new implementations during configuration.
351
-
352
- ### Custom Server Renderer
353
-
354
- `react-rails` depends on a renderer class for rendering components on the server. You can provide a custom renderer class to `config.react.server_renderer`. The class must implement:
355
-
356
- - `#initialize(options={})`, which accepts the hash from `config.react.server_renderer_options`
357
- - `#render(component_name, props, prerender_options)` to return a string of HTML
358
-
359
- `react-rails` provides two renderer classes: `React::ServerRendering::ExecJSRenderer` and `React::ServerRendering::SprocketsRenderer`.
360
-
361
- `ExecJSRenderer` offers two other points for extension:
362
-
363
- - `#before_render(component_name, props, prerender_options)` to return a string of JavaScript to execute _before_ calling `React.render`
364
- - `#after_render(component_name, props, prerender_options)` to return a string of JavaScript to execute _after_ calling `React.render`
365
-
366
- Any subclass of `ExecJSRenderer` may use those hooks (for example, `SprocketsRenderer` uses them to handle `console.*` on the server).
367
-
368
- ### Custom View Helper
369
-
370
- `react-rails` uses a "helper implementation" class to generate the output of the `react_component` helper. The helper is initialized once per request and used for each `react_component` call during that request. You can provide a custom helper class to `config.react.view_helper_implementation`. The class must implement:
371
-
372
- - `#react_component(name, props = {}, options = {}, &block)` to return a string to inject into the Rails view
373
- - `#setup(controller_instance)`, called when the helper is initialized at the start of the request
374
- - `#teardown(controller_instance)`, called at the end of the request
375
-
376
- `react-rails` provides one implementation, `React::Rails::ComponentMount`.
377
-
378
- ### Custom JSX Transformer
462
+ You can also specify this option in `react_component`:
379
463
 
380
- `react-rails` uses a transformer class to transform JSX for the browser. The transformer is initialized once, at boot. You can provide a custom transformer to `config.react.jsx_transformer_class`. The transformer must implement:
381
-
382
- - `#initialize(options)`, where options is the value passed to `config.react.jsx_transform_options`
383
- - `#transform(code_string)` to return a string of transformed code
384
-
385
- `react-rails` provides two transformers, `React::JSX::JSXTransformer` and `React::JSX::BabelTransformer`.
464
+ ```erb
465
+ <%= react_component('HelloMessage', {name: 'John'}, {camelize_props: true}) %>
466
+ ```
386
467
 
387
- ### Related Projects
468
+ ## Related Projects
388
469
 
389
- - [react\_on\_rails Gem](https://github.com/shakacode/react_on_rails): Webpack Integration of React with Rails utilizing the modern JavaScript tooling and libraries, including Webpack, Babel, React, Redux, React-Router. You can an example of this live at [www.reactrails.com](http://www.reactrails.com).
470
+ - [react\_on\_rails Gem](https://github.com/shakacode/react_on_rails): Integration of React with Rails utilizing Webpack, Babel, React, Redux, React-Router.
390
471
  - [Ruby Hyperloop](http://ruby-hyperloop.io/): Use Ruby to build reactive user interfaces with React.
391
472
  - [react-rails-hot-loader](https://github.com/rmosolgo/react-rails-hot-loader) is a simple live-reloader for `react-rails`.
392
473
  - [react-rails-benchmark_renderer](https://github.com/pboling/react-rails-benchmark_renderer) adds performance instrumentation to server rendering.
@@ -396,5 +477,4 @@ Any subclass of `ExecJSRenderer` may use those hooks (for example, `SprocketsRen
396
477
 
397
478
  - Run tests with `rake test` or `appraisal rake test`
398
479
  - Update React assets with `rake react:update`
399
-
400
- [React Getting Started]: https://facebook.github.io/react/docs/getting-started.html
480
+ - Update the UJS with `rake ujs:update`