react_on_rails_pro 16.2.0.beta.20 → 16.2.0.rc.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 +3 -1
- data/CONTRIBUTING.md +64 -43
- data/Gemfile.lock +4 -4
- data/README.md +1 -1
- data/docs/bundle-caching.md +22 -8
- data/docs/caching.md +39 -27
- data/docs/code-splitting-loadable-components.md +2 -2
- data/docs/code-splitting.md +74 -70
- data/docs/configuration.md +6 -6
- data/docs/contributors-info/onboarding-customers.md +2 -1
- data/docs/contributors-info/releasing.md +1 -0
- data/docs/contributors-info/style.md +23 -15
- data/docs/home-pro.md +33 -15
- data/docs/installation.md +51 -2
- data/docs/js-memory-leaks.md +2 -3
- data/docs/node-renderer/debugging.md +5 -1
- data/docs/node-renderer/error-reporting-and-tracing.md +27 -15
- data/docs/node-renderer/heroku.md +4 -5
- data/docs/profiling-server-side-rendering-code.md +43 -42
- data/docs/react-server-components/add-streaming-and-interactivity.md +1 -1
- data/docs/react-server-components/create-without-ssr.md +18 -18
- data/docs/react-server-components/glossary.md +22 -3
- data/docs/react-server-components/how-react-server-components-work.md +25 -18
- data/docs/react-server-components/inside-client-components.md +19 -18
- data/docs/react-server-components/purpose-and-benefits.md +24 -14
- data/docs/react-server-components/rendering-flow.md +7 -3
- data/docs/react-server-components/server-side-rendering.md +23 -22
- data/docs/release-notes/4.0.md +103 -94
- data/docs/release-notes/v4-react-server-components.md +16 -16
- data/docs/streaming-server-rendering.md +2 -4
- data/docs/troubleshooting.md +5 -2
- data/docs/updating.md +49 -13
- data/lib/react_on_rails_pro/request.rb +18 -3
- data/lib/react_on_rails_pro/version.rb +1 -1
- data/rakelib/dummy_apps.rake +4 -4
- data/rakelib/lint.rake +1 -1
- data/rakelib/run_rspec.rake +3 -3
- metadata +4 -4
data/docs/caching.md
CHANGED
|
@@ -4,25 +4,28 @@ Caching at the React on Rails level can greatly speed up your app and reduce the
|
|
|
4
4
|
|
|
5
5
|
Consult the [Rails Guide on Caching](http://guides.rubyonrails.org/caching_with_rails.html#cache-stores) for details on:
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
- [Cache Stores and Configuration](http://guides.rubyonrails.org/caching_with_rails.html#cache-stores)
|
|
8
|
+
- [Determination of Cache Keys](http://guides.rubyonrails.org/caching_with_rails.html#cache-keys)
|
|
9
|
+
- [Caching in Development](http://guides.rubyonrails.org/caching_with_rails.html#caching-in-development): **To toggle caching in development**, run `rails dev:cache`.
|
|
10
10
|
|
|
11
11
|
See the [bottom note on confirming and debugging cache keys](#confirming-and-debugging-cache-keys).
|
|
12
12
|
|
|
13
13
|
## Overview
|
|
14
|
+
|
|
14
15
|
React on Rails Pro has caching at 2 levels:
|
|
15
16
|
|
|
16
|
-
1. "Fragment caching" view helpers, `cached_react_component` and `cached_react_component_hash`.
|
|
17
|
-
2. Caching of requests for server rendering.
|
|
17
|
+
1. "Fragment caching" view helpers, `cached_react_component` and `cached_react_component_hash`.
|
|
18
|
+
2. Caching of requests for server rendering.
|
|
18
19
|
|
|
19
20
|
### Tracing
|
|
21
|
+
|
|
20
22
|
If tracing is turned on in your config/initializers/react_on_rails_pro.rb, you'll see timing log messages that begin with `[ReactOnRailsPro:1234]: exec_server_render_js` where 1234 is the process id and `exec_server_render_js` could be a different method being traced.
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
- **exec_server_render_js**: Timing of server rendering, which may have the prerender_caching turned on.
|
|
25
|
+
- **cached_react_component** and **cached_react_component_hash**: Timing of the cached view helper which may be calling server rendering.
|
|
24
26
|
|
|
25
27
|
Here's a sample. Note the second request
|
|
28
|
+
|
|
26
29
|
```
|
|
27
30
|
Started GET "/server_side_redux_app_cached" for ::1 at 2018-05-24 22:40:13 -1000
|
|
28
31
|
[ReactOnRailsPro:63422] exec_server_render_js: ReduxApp, 230.7ms
|
|
@@ -40,16 +43,19 @@ Completed 200 OK in 19ms (Views: 16.4ms | ActiveRecord: 0.0ms)
|
|
|
40
43
|
## Prerender (Server Side Rendering) Caching
|
|
41
44
|
|
|
42
45
|
### Why?
|
|
43
|
-
|
|
46
|
+
|
|
47
|
+
1. Server-side rendering is typically done like a stateless functional component, meaning that the result should be idempotent based on props passed in.
|
|
44
48
|
1. It's much easier than configuring fragment caching. So long as you have some space in your Rails cache, "it should just work."
|
|
45
49
|
|
|
46
50
|
### Why not?
|
|
47
|
-
If you're using regular caching for most componentas (cached_react_component_hash), and you don't want to use caching for other components, then having prerender caching still results in caching for all your rendering calls, increasing the liklihood of premature cache ejection.
|
|
48
51
|
|
|
49
|
-
|
|
52
|
+
If you're using regular caching for most components (cached_react_component_hash), and you don't want to use caching for other components, then having prerender caching still results in caching for all your rendering calls, increasing the likelihood of premature cache ejection.
|
|
53
|
+
|
|
54
|
+
In the future, React on Rails will allow stateful server rendering. Thus, your server-side JavaScript depend on externalities, such as AJAX calls for
|
|
50
55
|
GraphQL. In that case, you will set this caching to false.
|
|
51
56
|
|
|
52
57
|
### When?
|
|
58
|
+
|
|
53
59
|
The largest percentage gains will come from saving the time of server rendering. However, even when not doing server rendering, caching can be effective as the caching will prevent the calculation of the props and the conversion to a string of the prop values.
|
|
54
60
|
|
|
55
61
|
### How?
|
|
@@ -67,6 +73,7 @@ Server rendering JavaScript evaluation requests are cached by a cache key that c
|
|
|
67
73
|
2. The JavaScript code to evaluate.
|
|
68
74
|
|
|
69
75
|
### Diagnostics
|
|
76
|
+
|
|
70
77
|
if you're using `react_component_hash`, you'll get 2 extra keys returned:
|
|
71
78
|
|
|
72
79
|
1. RORP_CACHE_KEY: the prerender cache key
|
|
@@ -76,7 +83,7 @@ It can be useful to log these to the rendered HTML page to debug caching issues.
|
|
|
76
83
|
|
|
77
84
|
## React on Rails Fragment Caching
|
|
78
85
|
|
|
79
|
-
This is very similar to Rails fragment caching.
|
|
86
|
+
This is very similar to Rails fragment caching.
|
|
80
87
|
|
|
81
88
|
From the [Rails docs](http://guides.rubyonrails.org/caching_with_rails.html#fragment-caching):
|
|
82
89
|
|
|
@@ -92,19 +99,22 @@ If you're already familiar with Rails fragment caching, the React on Rails imple
|
|
|
92
99
|
The reasons "why" and "why not" are the same as for basic Rails fragment caching:
|
|
93
100
|
|
|
94
101
|
### Why Use Fragment Caching?
|
|
102
|
+
|
|
95
103
|
1. Next to caching at the controller or HTTP level, this is the fastest type of caching.
|
|
96
104
|
2. The additional complexity to add this with React on Rails Pro is minimal.
|
|
97
105
|
3. The performance gains can be huge.
|
|
98
106
|
4. The load on your Rails server can be far lessened.
|
|
99
107
|
|
|
100
108
|
### Why Not Use Fragment Caching?
|
|
109
|
+
|
|
101
110
|
1. It's tricky to get all the right cache keys. You have to consider any values that can change and cause the rendering to change. See the [Rails docs for cache keys](http://guides.rubyonrails.org/caching_with_rails.html#cache-keys)
|
|
102
111
|
2. Testing is a bit tricky or just not done for fragment caching.
|
|
103
112
|
3. Some deployments require you to clear caches.
|
|
104
113
|
|
|
105
114
|
### Considerations for Determining Your Cache Key
|
|
115
|
+
|
|
106
116
|
1. Consult the [Rails docs for cache keys](http://guides.rubyonrails.org/caching_with_rails.html#cache-keys) for help with cache key definitions.
|
|
107
|
-
2. If your React code depends on any values from the [Rails Context](https://github.com/shakacode/react_on_rails/blob/master/docs/
|
|
117
|
+
2. If your React code depends on any values from the [Rails Context](https://github.com/shakacode/react_on_rails/blob/master/docs/core-concepts/render-functions-and-railscontext.md#rails-context), such as the `locale` or the URL `location`, then be sure to include such values in your cache key. In other words, if you are using some JavaScript such as `react-router` that depends on your URL, or on a call to `toLocalString(locale)`, then be sure to include such values in your cache key. To find the values that React on Rails uses, use some code like this:
|
|
108
118
|
|
|
109
119
|
```ruby
|
|
110
120
|
the_rails_context = rails_context
|
|
@@ -114,14 +124,14 @@ location = the_rails_context[:location]
|
|
|
114
124
|
|
|
115
125
|
If you are calling `rails_context` from your controller method, then prefix it like this: `helpers.rails_context` so long as you have react_on_rails > 11.2.2. If less than that, call `helpers.send(:rails_context, server_side: true)`
|
|
116
126
|
|
|
117
|
-
|
|
118
|
-
If performance is particulary sensitive, consult the view helper definition for `rails_context`. For example, you can save the cost of calculating the rails_context by directly getting a value:
|
|
127
|
+
If performance is particularly sensitive, consult the view helper definition for `rails_context`. For example, you can save the cost of calculating the rails_context by directly getting a value:
|
|
119
128
|
|
|
120
129
|
```ruby
|
|
121
130
|
i18nLocale = I18n.locale
|
|
122
131
|
```
|
|
123
|
-
|
|
132
|
+
|
|
124
133
|
### How: API
|
|
134
|
+
|
|
125
135
|
Here is the doc for helpers `cached_react_component` and `cached_react_component_hash`. Consult the [docs in React on Rails](https://www.shakacode.com/react-on-rails/docs/api/view-helpers-api/) for the non-cached analogies `react_component` and `react_component_hash`. These docs only show the differences.
|
|
126
136
|
|
|
127
137
|
```ruby
|
|
@@ -131,10 +141,10 @@ Here is the doc for helpers `cached_react_component` and `cached_react_component
|
|
|
131
141
|
# 1. You must pass the props as a block. This is so that the evaluation of the props is not done
|
|
132
142
|
# if the cache can be used.
|
|
133
143
|
# 2. Provide the cache_key option
|
|
134
|
-
# cache_key: String or Array (or Proc returning a String or Array) containing your cache keys.
|
|
135
|
-
# If prerender is set to true, the server bundle digest will be included in the cache key.
|
|
144
|
+
# cache_key: String or Array (or Proc returning a String or Array) containing your cache keys.
|
|
145
|
+
# If prerender is set to true, the server bundle digest will be included in the cache key.
|
|
136
146
|
# The cache_key value is the same as used for conventional Rails fragment caching.
|
|
137
|
-
# 3. Optionally provide the `:cache_options` key with a value of a hash including as
|
|
147
|
+
# 3. Optionally provide the `:cache_options` key with a value of a hash including as
|
|
138
148
|
# :compress, :expires_in, :race_condition_ttl as documented in the Rails Guides
|
|
139
149
|
# 4. Provide boolean values for `:if` or `:unless` to conditionally use caching.
|
|
140
150
|
```
|
|
@@ -144,6 +154,7 @@ You can find the `:cache_options` documented in the [Rails docs for ActiveSuppor
|
|
|
144
154
|
#### API Usage examples
|
|
145
155
|
|
|
146
156
|
The fragment caching for `react_component`:
|
|
157
|
+
|
|
147
158
|
```ruby
|
|
148
159
|
<%= cached_react_component("App", cache_key: [@user, @post], prerender: true) do
|
|
149
160
|
some_slow_method_that_returns_props
|
|
@@ -151,6 +162,7 @@ end %>
|
|
|
151
162
|
```
|
|
152
163
|
|
|
153
164
|
Suppose you only want to cache when `current_user.nil?`. Use the `:if` option (`unless:` is analogous):
|
|
165
|
+
|
|
154
166
|
```ruby
|
|
155
167
|
<%= cached_react_component("App", cache_key: [@user, @post], prerender: true, if: current_user.nil?) do
|
|
156
168
|
some_slow_method_that_returns_props
|
|
@@ -173,11 +185,11 @@ And a fragment caching version for the `react_component_hash`:
|
|
|
173
185
|
|
|
174
186
|
<% printable_cache_key = ReactOnRailsPro::Utils.printable_cache_key(result[:RORP_CACHE_KEY]) %>
|
|
175
187
|
<!-- <%= "CACHE_HIT: #{result[:RORP_CACHE_HIT]}, RORP_CACHE_KEY: #{printable_cache_key}" %> -->
|
|
176
|
-
|
|
177
|
-
Note in the above example, React on Rails Pro returns both the raw cache key and whether or not there was a cache hit.
|
|
188
|
+
```
|
|
178
189
|
|
|
190
|
+
Note in the above example, React on Rails Pro returns both the raw cache key and whether or not there was a cache hit.
|
|
179
191
|
|
|
180
|
-
### Your JavaScript Bundles and Cache Keys
|
|
192
|
+
### Your JavaScript Bundles and Cache Keys
|
|
181
193
|
|
|
182
194
|
When doing fragment caching of server rendering with React on Rails Pro, the cache key must reflect
|
|
183
195
|
your React. This is analogous to how Rails puts an MD5 hash of your views in
|
|
@@ -191,15 +203,17 @@ case you have, React on Rails handles it.
|
|
|
191
203
|
|
|
192
204
|
# Confirming and Debugging Cache Keys
|
|
193
205
|
|
|
194
|
-
Cache key composition can be confirmed in development mode with the following steps.
|
|
206
|
+
Cache key composition can be confirmed in development mode with the following steps. The goal is to confirm that some change that should trigger new cached data actually triggers a new cache key. For example, when the server bundle changes, does that trigger a new cache key for any server rendering?
|
|
195
207
|
|
|
196
208
|
1. Run `Rails.cache.clear` to clear the cache.
|
|
197
|
-
1. Run `rails dev:cache` to toggle caching in development mode.
|
|
209
|
+
1. Run `rails dev:cache` to toggle caching in development mode.
|
|
198
210
|
|
|
199
211
|
You will see a message like:
|
|
212
|
+
|
|
200
213
|
> Development mode is now being cached.
|
|
201
214
|
|
|
202
215
|
You might need to check your `config/development.rb`contains the following:
|
|
216
|
+
|
|
203
217
|
```ruby
|
|
204
218
|
# Enable/disable caching. By default caching is disabled.
|
|
205
219
|
if Rails.root.join("tmp/caching-dev.txt").exist?
|
|
@@ -209,7 +223,7 @@ You might need to check your `config/development.rb`contains the following:
|
|
|
209
223
|
config.public_file_server.headers = {
|
|
210
224
|
"Cache-Control" => "public, max-age=172800"
|
|
211
225
|
}
|
|
212
|
-
|
|
226
|
+
|
|
213
227
|
# For Rails >= 5.1 determines whether to log fragment cache reads and writes in verbose format as follows:
|
|
214
228
|
config.action_controller.enable_fragment_cache_logging
|
|
215
229
|
else
|
|
@@ -225,10 +239,8 @@ You might need to check your `config/development.rb`contains the following:
|
|
|
225
239
|
|
|
226
240
|
5. Check the cache entry again. You should have noticed that it changed.
|
|
227
241
|
|
|
228
|
-
|
|
229
242
|
To avoid seeing the cache calls to the prerender_caching, you can temporarily set:
|
|
243
|
+
|
|
230
244
|
```
|
|
231
245
|
config.prerender_caching = false
|
|
232
246
|
```
|
|
233
|
-
|
|
234
|
-
|
|
@@ -167,7 +167,7 @@ So, hydration will be fired only after all necessary chunks preloads. In this ex
|
|
|
167
167
|
|
|
168
168
|
```js
|
|
169
169
|
import React from 'react';
|
|
170
|
-
import ReactOnRails from 'react-on-rails';
|
|
170
|
+
import ReactOnRails from 'react-on-rails-pro';
|
|
171
171
|
import { hydrateRoot } from 'react-dom/client';
|
|
172
172
|
import { loadableReady } from '@loadable/component';
|
|
173
173
|
import App from './App';
|
|
@@ -191,7 +191,7 @@ style tags to the Rails view. In this example below, `ServerApp` is registering
|
|
|
191
191
|
|
|
192
192
|
```js
|
|
193
193
|
import React from 'react';
|
|
194
|
-
import ReactOnRails from 'react-on-rails';
|
|
194
|
+
import ReactOnRails from 'react-on-rails-pro';
|
|
195
195
|
import { ChunkExtractor } from '@loadable/server';
|
|
196
196
|
import App from './App';
|
|
197
197
|
import path from 'path';
|
data/docs/code-splitting.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# Server-side rendering with code-splitting in React on Rails
|
|
2
|
+
|
|
2
3
|
by ShakaCode
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
_Last updated June 13, 2019_
|
|
5
6
|
|
|
6
7
|
# Deprecated
|
|
7
8
|
|
|
@@ -21,14 +22,16 @@ If the project includes server rendering, then you need to exclude the use of dy
|
|
|
21
22
|
# Dependencies
|
|
22
23
|
|
|
23
24
|
Install following libraries in client folder:
|
|
25
|
+
|
|
24
26
|
```
|
|
25
27
|
yarn add react-loadable webpack-conditional-loader
|
|
26
28
|
```
|
|
27
29
|
|
|
28
30
|
- [react-loadable](https://github.com/jamiebuilds/react-loadable) - take cares of loading and correctly displaying our dynamic components.
|
|
29
|
-
- [webpack-conditional-loader](https://www.npmjs.com/package/webpack-conditional-loader) - allow us conditionally extract parts of our code into different bundles.
|
|
31
|
+
- [webpack-conditional-loader](https://www.npmjs.com/package/webpack-conditional-loader) - allow us conditionally extract parts of our code into different bundles.
|
|
30
32
|
|
|
31
33
|
Add `webpack-conditional-loader` to the loaders, like this:
|
|
34
|
+
|
|
32
35
|
```js
|
|
33
36
|
{
|
|
34
37
|
test: /\.jsx?$/,
|
|
@@ -45,6 +48,7 @@ Add `webpack-conditional-loader` to the loaders, like this:
|
|
|
45
48
|
```
|
|
46
49
|
|
|
47
50
|
Optionally. Create alias for `DynamicImports.js` file in `resolve`:
|
|
51
|
+
|
|
48
52
|
```js
|
|
49
53
|
alias: {
|
|
50
54
|
DynamicImports: path.resolve(__dirname, 'client', 'DynamicImports.js'),
|
|
@@ -54,6 +58,7 @@ alias: {
|
|
|
54
58
|
# Simple example of using dynamic components
|
|
55
59
|
|
|
56
60
|
Consider the component that we want to convert to a dynamic:
|
|
61
|
+
|
|
57
62
|
```
|
|
58
63
|
components
|
|
59
64
|
|_ Map
|
|
@@ -61,6 +66,7 @@ components
|
|
|
61
66
|
```
|
|
62
67
|
|
|
63
68
|
Let's create `index.jsx` in `Map` directory with the following contents:
|
|
69
|
+
|
|
64
70
|
```jsx
|
|
65
71
|
let Component = null;
|
|
66
72
|
|
|
@@ -84,16 +90,16 @@ import Loadable from 'react-loadable';
|
|
|
84
90
|
|
|
85
91
|
import Loading from '../Loading';
|
|
86
92
|
|
|
87
|
-
const load = opts =>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
const load = (opts) =>
|
|
94
|
+
Loadable({
|
|
95
|
+
delay: 10000,
|
|
96
|
+
loading: () => <Loading />,
|
|
97
|
+
render(loaded, props) {
|
|
98
|
+
const LoadedComponent = loaded.default;
|
|
99
|
+
return <LoadedComponent {...props} />;
|
|
100
|
+
},
|
|
101
|
+
...opts,
|
|
102
|
+
});
|
|
97
103
|
|
|
98
104
|
/* Here we're wrapping our component in react-loadable HOC */
|
|
99
105
|
const DynamicComponent = load({
|
|
@@ -101,7 +107,7 @@ const DynamicComponent = load({
|
|
|
101
107
|
We need to specify these params: `webpackChunkName`, `modules` and `webpack`
|
|
102
108
|
so react-loadable can load our chunk correctly
|
|
103
109
|
*/
|
|
104
|
-
loader: () => import(/* webpackChunkName: "Map" */'./Map'),
|
|
110
|
+
loader: () => import(/* webpackChunkName: "Map" */ './Map'),
|
|
105
111
|
modules: ['./Map'],
|
|
106
112
|
webpack: () => [require.resolveWeak('./Map')],
|
|
107
113
|
});
|
|
@@ -116,12 +122,15 @@ export default Component;
|
|
|
116
122
|
```
|
|
117
123
|
|
|
118
124
|
Now, if we want to use this component we should import it like this:
|
|
125
|
+
|
|
119
126
|
```jsx
|
|
120
|
-
import Map from './components/Map'
|
|
127
|
+
import Map from './components/Map';
|
|
121
128
|
```
|
|
129
|
+
|
|
122
130
|
in this case, webpack will load `index.jsx` instead of `Map.jsx` if not some other special order specified.
|
|
123
131
|
|
|
124
132
|
Also, `IS_SSR=true` must added when creating server side bundle, like this:
|
|
133
|
+
|
|
125
134
|
```
|
|
126
135
|
NODE_ENV=production IS_SSR=true webpack --config webpack.config.ssr.prod.js
|
|
127
136
|
```
|
|
@@ -141,33 +150,34 @@ More details can be found in the documentation react-loadable.
|
|
|
141
150
|
|
|
142
151
|
But we can get rid of annoying flickering `Loading ...` the first time the page loads. The server renderer has already rendered the necessary components. Therefore, we can transfer this information from the server renderer to the client and preload the necessary modules.
|
|
143
152
|
|
|
144
|
-
Unfortunately, the way specified in the documentation `react-loadable` does not work for us.
|
|
153
|
+
Unfortunately, the way specified in the documentation `react-loadable` does not work for us.
|
|
145
154
|
|
|
146
155
|
Here is another similar method.
|
|
147
156
|
|
|
148
157
|
For this we use the function `registerDynamicComponentOnServer`. We will place it in the new file `DynamicImports.js`:
|
|
149
158
|
|
|
150
|
-
|
|
151
159
|
```javascript
|
|
152
|
-
export const registerDynamicComponentOnServer = name => {
|
|
153
|
-
const serverSide = typeof window === 'undefined'
|
|
160
|
+
export const registerDynamicComponentOnServer = (name) => {
|
|
161
|
+
const serverSide = typeof window === 'undefined';
|
|
154
162
|
|
|
155
163
|
if (serverSide) {
|
|
156
164
|
if (typeof global.dynamicComponents === 'undefined') {
|
|
157
|
-
global.dynamicComponents = []
|
|
165
|
+
global.dynamicComponents = [];
|
|
158
166
|
}
|
|
159
167
|
if (global.dynamicComponents.indexOf(name) === -1) {
|
|
160
|
-
global.dynamicComponents.push(name)
|
|
168
|
+
global.dynamicComponents.push(name);
|
|
161
169
|
}
|
|
162
170
|
}
|
|
163
|
-
}
|
|
171
|
+
};
|
|
164
172
|
```
|
|
173
|
+
|
|
165
174
|
As you can see from the function body, it runs only for server-side rendering.
|
|
166
175
|
It simply adds the name of the component to the global array `dynamicComponents` which will be transferred to the client later.
|
|
167
176
|
|
|
168
177
|
It must be imported into the component that needs to be made dynamic and called in the render method of this component. For example:
|
|
169
178
|
|
|
170
179
|
components/Map/Map.jsx:
|
|
180
|
+
|
|
171
181
|
```javascript
|
|
172
182
|
...
|
|
173
183
|
import { registerDynamicComponentOnServer } from 'DynamicImports';
|
|
@@ -182,39 +192,33 @@ class Map extends React.Component {
|
|
|
182
192
|
}
|
|
183
193
|
```
|
|
184
194
|
|
|
185
|
-
|
|
186
195
|
Then this global array must be passed to the client.
|
|
187
196
|
To do this, change server entry point as follows:
|
|
188
197
|
|
|
189
|
-
|
|
190
198
|
**ServerApp.js**:
|
|
199
|
+
|
|
191
200
|
```javascript
|
|
192
201
|
import React from 'react';
|
|
193
|
-
import ReactOnRails from 'react-on-rails';
|
|
202
|
+
import ReactOnRails from 'react-on-rails-pro';
|
|
194
203
|
|
|
195
204
|
import App from './App';
|
|
196
205
|
|
|
197
206
|
const ServerApp = (props, railsContext) => {
|
|
198
|
-
|
|
199
|
-
const html = renderToString(
|
|
200
|
-
<App
|
|
201
|
-
{...props}
|
|
202
|
-
components={{ MainPage, AboutPage }}
|
|
203
|
-
/>
|
|
204
|
-
);
|
|
207
|
+
const html = renderToString(<App {...props} components={{ MainPage, AboutPage }} />);
|
|
205
208
|
|
|
206
209
|
return {
|
|
207
210
|
html,
|
|
208
211
|
dynamicComponents: JSON.stringify(global.dynamicComponents),
|
|
209
212
|
};
|
|
210
|
-
}
|
|
213
|
+
};
|
|
211
214
|
|
|
212
|
-
ReactOnRails.register({ App: ServerApp })
|
|
215
|
+
ReactOnRails.register({ App: ServerApp });
|
|
213
216
|
|
|
214
|
-
export default ServerApp
|
|
217
|
+
export default ServerApp;
|
|
215
218
|
```
|
|
216
219
|
|
|
217
220
|
And add our array to view in rails, where our react_component is displayed
|
|
221
|
+
|
|
218
222
|
```slim
|
|
219
223
|
<% component = react_component("App", props: {}, prerender: true) %>
|
|
220
224
|
|
|
@@ -237,90 +241,87 @@ To do this, we will create an object with the component names as the keys, and t
|
|
|
237
241
|
We will add it to `DynamicImports.js` and add a check for the presence of the registered component in this object in the function` registerDynamicComponentOnServer`:
|
|
238
242
|
|
|
239
243
|
**DynamicImports.js**
|
|
244
|
+
|
|
240
245
|
```javascript
|
|
241
246
|
const DynamicImports = {
|
|
242
|
-
Map: () => import('./components/Map')
|
|
243
|
-
}
|
|
247
|
+
Map: () => import('./components/Map'),
|
|
248
|
+
};
|
|
244
249
|
|
|
245
|
-
export const registerDynamicComponentOnServer = name => {
|
|
246
|
-
const serverSide = typeof window === 'undefined'
|
|
250
|
+
export const registerDynamicComponentOnServer = (name) => {
|
|
251
|
+
const serverSide = typeof window === 'undefined';
|
|
247
252
|
|
|
248
253
|
if (serverSide) {
|
|
249
254
|
if (typeof global.dynamicComponents === 'undefined') {
|
|
250
|
-
global.dynamicComponents = []
|
|
255
|
+
global.dynamicComponents = [];
|
|
251
256
|
}
|
|
252
257
|
if (typeof DynamicImports[name] === 'undefined') {
|
|
253
|
-
throw new Error(`Dynamic import not defined for ${name}`)
|
|
258
|
+
throw new Error(`Dynamic import not defined for ${name}`);
|
|
254
259
|
}
|
|
255
260
|
if (global.dynamicComponents.indexOf(name) === -1) {
|
|
256
|
-
global.dynamicComponents.push(name)
|
|
261
|
+
global.dynamicComponents.push(name);
|
|
257
262
|
}
|
|
258
263
|
}
|
|
259
|
-
}
|
|
264
|
+
};
|
|
260
265
|
|
|
261
|
-
export default DynamicImports
|
|
266
|
+
export default DynamicImports;
|
|
262
267
|
```
|
|
263
268
|
|
|
264
269
|
Now we can load the component we need, knowing its name
|
|
265
270
|
For example:
|
|
271
|
+
|
|
266
272
|
```javascript
|
|
267
|
-
DynamicImports
|
|
273
|
+
DynamicImports['Map']();
|
|
268
274
|
```
|
|
275
|
+
|
|
269
276
|
This function will return Promise, which can be used for client rendering.
|
|
270
277
|
|
|
271
278
|
Change the Client.js to add the preloading of the required components:
|
|
272
279
|
|
|
273
|
-
|
|
274
280
|
**Client.js**
|
|
281
|
+
|
|
275
282
|
```javascript
|
|
276
283
|
import React from 'react';
|
|
277
284
|
import { hydrateRoot } from 'react-dom/client';
|
|
278
|
-
import Loadable from 'react-loadable'
|
|
285
|
+
import Loadable from 'react-loadable';
|
|
279
286
|
|
|
280
287
|
import App from './App';
|
|
281
288
|
|
|
282
|
-
import DynamicImports from 'DynamicImports'
|
|
289
|
+
import DynamicImports from 'DynamicImports';
|
|
283
290
|
|
|
284
291
|
const App = (props, railsContext, domNodeId) => {
|
|
285
|
-
|
|
286
292
|
const dynamicComponents =
|
|
287
|
-
typeof window.dynamicComponents !== 'undefined'
|
|
288
|
-
? JSON.parse(window.dynamicComponents)
|
|
289
|
-
: []
|
|
290
|
-
|
|
293
|
+
typeof window.dynamicComponents !== 'undefined' ? JSON.parse(window.dynamicComponents) : [];
|
|
291
294
|
|
|
292
|
-
const dynamicImports = []
|
|
293
|
-
dynamicComponents.map(name => {
|
|
294
|
-
const dynamicImportInvoked = DynamicImports[name]()
|
|
295
|
-
dynamicImports.push(dynamicImportInvoked)
|
|
296
|
-
})
|
|
295
|
+
const dynamicImports = [];
|
|
296
|
+
dynamicComponents.map((name) => {
|
|
297
|
+
const dynamicImportInvoked = DynamicImports[name]();
|
|
298
|
+
dynamicImports.push(dynamicImportInvoked);
|
|
299
|
+
});
|
|
297
300
|
|
|
298
301
|
Promise.all(dynamicImports)
|
|
299
302
|
.then(() => Loadable.preloadReady())
|
|
300
303
|
.then(() => {
|
|
301
304
|
hydrateRoot(
|
|
302
|
-
<App
|
|
303
|
-
{...props}
|
|
304
|
-
components={{ MainPage, AboutPage }}
|
|
305
|
-
/>,
|
|
305
|
+
<App {...props} components={{ MainPage, AboutPage }} />,
|
|
306
306
|
document.getElementById(domNodeId),
|
|
307
|
-
)
|
|
308
|
-
})
|
|
309
|
-
}
|
|
307
|
+
);
|
|
308
|
+
});
|
|
309
|
+
};
|
|
310
310
|
|
|
311
|
-
export default App
|
|
311
|
+
export default App;
|
|
312
312
|
```
|
|
313
313
|
|
|
314
314
|
This code requires explanation.
|
|
315
315
|
|
|
316
316
|
The array with names of rendered components called `dynamicComponents` is used in the map function.
|
|
317
317
|
In this function, the dynamic import invoked and the result (promise) is added to `dynamicImports` array.
|
|
318
|
+
|
|
318
319
|
```javascript
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
320
|
+
const dynamicImports = [];
|
|
321
|
+
dynamicComponents.map((name) => {
|
|
322
|
+
const dynamicImportInvoked = DynamicImports[name]();
|
|
323
|
+
dynamicImports.push(dynamicImportInvoked);
|
|
324
|
+
});
|
|
324
325
|
```
|
|
325
326
|
|
|
326
327
|
This array is used in the function `Promise.all`
|
|
@@ -334,16 +335,19 @@ Then fires `Loadable.preloadReady()`
|
|
|
334
335
|
```javascript
|
|
335
336
|
.then(() => Loadable.preloadReady())
|
|
336
337
|
```
|
|
338
|
+
|
|
337
339
|
As in the doc:
|
|
338
340
|
Check for modules that are already loaded in the browser and call the matching LoadableComponent.preload methods.
|
|
339
341
|
|
|
340
342
|
We need to call this method to initialize already preloaded components.
|
|
341
343
|
|
|
342
344
|
In addition, note that in the creation of dynamic modules, the `modules` and` webpack` options are used, per the docs for react-loadable.
|
|
345
|
+
|
|
343
346
|
```javascript
|
|
344
347
|
modules: ['./AboutPage'],
|
|
345
348
|
webpack: () => [require.resolveWeak('./AboutPage')],
|
|
346
349
|
```
|
|
350
|
+
|
|
347
351
|
They are needed to make .preload method work properly
|
|
348
352
|
|
|
349
353
|
Thus, all dynamic modules will be loaded up to hydrate, and there will be no flicker.
|
data/docs/configuration.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Configuration
|
|
2
2
|
|
|
3
|
-
`config/initializers/react_on_rails_pro.rb`
|
|
3
|
+
`config/initializers/react_on_rails_pro.rb`
|
|
4
4
|
|
|
5
5
|
1. You don't need to create a initializer if you are satisfied with the defaults as described below.
|
|
6
6
|
1. Values beginning with `renderer` pertain only to using an external rendering server. You will need to ensure these values are consistent with your configuration for the external rendering server, as given in [JS configuration](https://www.shakacode.com/react-on-rails-pro/docs/node-renderer/js-configuration/)
|
|
@@ -32,12 +32,12 @@ ReactOnRailsPro.configure do |config|
|
|
|
32
32
|
# Remote bundle caching saves deployment time by caching bundles.
|
|
33
33
|
# See /docs/bundle-caching.md for usage and an example of a module called S3BundleCacheAdapter.
|
|
34
34
|
config.remote_bundle_cache_adapter = nil
|
|
35
|
-
|
|
35
|
+
|
|
36
36
|
# ALL OPTIONS BELOW ONLY APPLY IF SERVER RENDERING
|
|
37
37
|
|
|
38
38
|
# If true, then cache the evaluation of JS for prerendering using the standard Rails cache.
|
|
39
39
|
# Applies to all rendering engines.
|
|
40
|
-
# Default for `prerender_caching` is false.
|
|
40
|
+
# Default for `prerender_caching` is false.
|
|
41
41
|
config.prerender_caching = true
|
|
42
42
|
|
|
43
43
|
# Retry request in case of time out on the node-renderer side
|
|
@@ -77,9 +77,9 @@ ReactOnRailsPro.configure do |config|
|
|
|
77
77
|
# config.renderer_password = ENV["RENDERER_PASSWORD"]
|
|
78
78
|
|
|
79
79
|
# Set the `ssr_timeout` configuration so the Rails server will not wait more than this many seconds
|
|
80
|
-
# for a SSR request to return once issued.
|
|
80
|
+
# for a SSR request to return once issued.
|
|
81
81
|
config.ssr_timeout = 5
|
|
82
|
-
|
|
82
|
+
|
|
83
83
|
# If false, then crash if no backup rendering when the remote renderer is not available
|
|
84
84
|
# Can be useful to set to false in development or testing to make sure that the remote renderer
|
|
85
85
|
# works and any non-availability of the remote renderer does not just do ExecJS.
|
|
@@ -103,7 +103,7 @@ ReactOnRailsPro.configure do |config|
|
|
|
103
103
|
config.renderer_http_pool_warn_timeout = 0.25 # seconds
|
|
104
104
|
|
|
105
105
|
# Snippet of JavaScript to be run right at the beginning of the server rendering process. The code
|
|
106
|
-
# to be executed must either be self contained or reference some globally exposed module.
|
|
106
|
+
# to be executed must either be self contained or reference some globally exposed module.
|
|
107
107
|
# For example, suppose that we had to call `SomeLibrary.clearCache()`between every call to server
|
|
108
108
|
# renderer to ensure no leakage of state between calls. Note, SomeLibrary needs to be globally
|
|
109
109
|
# exposed in the server rendering webpack bundle. This code is visible in the tracing of the calls
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Creating a github OAuth Token
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
_[Document for ShakaCode Staff](https://docs.google.com/document/d/10snzXEWgkorcai76_OxlhjQcDae_WoxRfBCdVbmcQoU/edit)_
|
|
4
4
|
|
|
5
5
|
# Customer Steps
|
|
6
|
+
|
|
6
7
|
See [Installation](../installation.md).
|