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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +294 -214
- data/lib/assets/javascripts/react_ujs.js +429 -7
- data/lib/generators/react/component_generator.rb +24 -12
- data/lib/generators/react/install_generator.rb +76 -18
- data/lib/generators/templates/react_server_rendering.rb +2 -0
- data/lib/generators/templates/server_rendering.js +6 -0
- data/lib/generators/templates/server_rendering_pack.js +5 -0
- data/lib/react/jsx.rb +2 -0
- data/lib/react/rails/component_mount.rb +23 -5
- data/lib/react/rails/controller_lifecycle.rb +35 -7
- data/lib/react/rails/railtie.rb +17 -11
- data/lib/react/rails/version.rb +1 -1
- data/lib/react/server_rendering.rb +16 -4
- data/lib/react/server_rendering/{sprockets_renderer.rb → bundle_renderer.rb} +40 -20
- data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_polyfill.js +0 -0
- data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/console_replay.js +1 -1
- data/lib/react/server_rendering/bundle_renderer/console_reset.js +3 -0
- data/lib/react/server_rendering/{sprockets_renderer → bundle_renderer}/timeout_polyfill.js +0 -0
- data/lib/react/server_rendering/exec_js_renderer.rb +4 -1
- data/lib/react/server_rendering/webpacker_manifest_container.rb +34 -0
- data/lib/react/server_rendering/yaml_manifest_container.rb +1 -1
- metadata +16 -16
- data/lib/assets/javascripts/react_ujs_event_setup.js +0 -29
- data/lib/assets/javascripts/react_ujs_mount.js +0 -104
- data/lib/assets/javascripts/react_ujs_native.js +0 -18
- data/lib/assets/javascripts/react_ujs_pjax.js +0 -10
- data/lib/assets/javascripts/react_ujs_turbolinks.js +0 -9
- data/lib/assets/javascripts/react_ujs_turbolinks_classic.js +0 -10
- data/lib/assets/javascripts/react_ujs_turbolinks_classic_deprecated.js +0 -13
- data/lib/generators/react/ujs_generator.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e9fe44131627108a965e3ab9eb09d5dfd1880926
|
4
|
+
data.tar.gz: aabdd492d7f9dd5cf9e17d7f10ef8547749cb8ca
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e13b6d9112c96ca74ecc0f2fc78540fc6577a63f7e46ae2e5cb9fa481726d5f879d8f63a2e5f479c572398bea14c3b37bed4c45952175e287d6dfd02602a0f8
|
7
|
+
data.tar.gz: d8c03917cabcf1d59b399fb10162642f5e76ea31b8d4f08ef5d223a3e2c713250c536a421db8f44d480ffae16d20f15b79b015bc5950a3977ff8796aea194805
|
data/CHANGELOG.md
CHANGED
@@ -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
|
[](http://rubygems.org/gems/react-rails)
|
2
4
|
[](https://travis-ci.org/reactjs/react-rails)
|
3
5
|
[](https://gemnasium.com/reactjs/react-rails)
|
4
6
|
[](https://codeclimate.com/github/reactjs/react-rails)
|
5
7
|
[](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
|
-
|
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
|
-
|
13
|
-
in your Ruby on Rails (3.2+) application. `react-rails` can:
|
23
|
+
Install from Rubygems as `react-rails`.
|
14
24
|
|
15
|
-
|
16
|
-
-
|
17
|
-
|
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
|
-
|
29
|
+
Get started with `rails g react:install`:
|
23
30
|
|
24
|
-
|
31
|
+
```
|
32
|
+
$ rails g react:install
|
33
|
+
```
|
25
34
|
|
26
|
-
|
35
|
+
## Use with Webpacker
|
27
36
|
|
28
|
-
|
29
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
81
|
+
## Use with Asset Pipeline
|
39
82
|
|
40
|
-
|
41
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
97
|
+
Now, you can create React components in `.jsx` files:
|
56
98
|
|
57
|
-
|
99
|
+
```js
|
100
|
+
// app/assets/javascripts/components/post.jsx
|
58
101
|
|
59
|
-
|
60
|
-
|
102
|
+
window.Post = React.createClass({
|
103
|
+
render: function() {
|
104
|
+
return <h1>{this.props.title}</h1>
|
105
|
+
}
|
106
|
+
})
|
61
107
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
108
|
+
// or, equivalent:
|
109
|
+
class Post extends React.Component {
|
110
|
+
render() {
|
111
|
+
return <h1>{this.props.title}</h1>
|
112
|
+
}
|
113
|
+
}
|
114
|
+
```
|
67
115
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
122
|
+
Components must be accessible from the top level, but they may be namespaced, for example:
|
75
123
|
|
76
|
-
```
|
77
|
-
|
78
|
-
|
79
|
-
end
|
124
|
+
```erb
|
125
|
+
<%= react_component("Comments.NewForm", {post_id: @post.id}) %>
|
126
|
+
<!-- looks for `window.Comments.NewForm` -->
|
80
127
|
```
|
81
128
|
|
82
|
-
|
83
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
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
|
-
|
138
|
+
### React.js versions
|
92
139
|
|
93
|
-
|
140
|
+
`//= require react` brings `React` into your project.
|
94
141
|
|
95
|
-
|
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.
|
100
|
-
|
101
|
-
|
102
|
-
|
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
|
-
|
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
|
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
|
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="{"name":"John"}"></div>
|
119
179
|
```
|
120
180
|
|
121
|
-
On page load, the
|
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
|
140
|
-
- `props` is either
|
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
|
-
|
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
|
-
|
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
|
-
|
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 `
|
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
|
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
|
-
|
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::
|
295
|
+
config.react.server_renderer = React::ServerRendering::BundleRenderer
|
187
296
|
config.react.server_renderer_options = {
|
188
|
-
files: ["
|
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
|
-
|
195
|
-
|
196
|
-
|
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
|
-
|
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
|
-
|
202
|
-
|
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
|
-
|
324
|
+
Then, you can access the ExecJS context directly with `react_rails_prerenderer.context`:
|
207
325
|
|
208
|
-
```
|
209
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
372
|
+
Prerendering is set to `true` by default, but can be turned off with `prerender: false`.
|
230
373
|
|
231
|
-
|
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
|
-
|
376
|
+
You can generate a new component file with:
|
238
377
|
|
239
|
-
```
|
240
|
-
rails
|
378
|
+
```sh
|
379
|
+
rails g react:component ComponentName prop1:type prop2:type ...
|
241
380
|
```
|
242
381
|
|
243
|
-
|
382
|
+
For example,
|
244
383
|
|
245
|
-
```
|
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
|
-
|
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
|
-
|
412
|
+
- `--es6`: use `class ComponentName extends React.Component`
|
413
|
+
- `--coffee`: use CoffeeScript
|
288
414
|
|
289
|
-
|
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
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
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
|
-
|
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
|
-
|
444
|
+
### Camelize Props
|
329
445
|
|
330
|
-
|
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
|
-
```
|
335
|
-
|
336
|
-
|
337
|
-
|
448
|
+
```ruby
|
449
|
+
MyApp::Application.configure do
|
450
|
+
config.react.camelize_props = true # default false
|
451
|
+
end
|
338
452
|
```
|
339
453
|
|
340
|
-
|
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
|
-
```
|
343
|
-
|
344
|
-
|
345
|
-
|
456
|
+
```ruby
|
457
|
+
{ all_todos: @todos, current_status: @status }
|
458
|
+
# becomes:
|
459
|
+
{ "allTodos" => @todos, "currentStatus" => @status }
|
346
460
|
```
|
347
461
|
|
348
|
-
|
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
|
-
|
381
|
-
|
382
|
-
|
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
|
-
|
468
|
+
## Related Projects
|
388
469
|
|
389
|
-
- [react\_on\_rails Gem](https://github.com/shakacode/react_on_rails):
|
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`
|