react-rails 1.11.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![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
|
-
|
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`
|