react_on_rails_pro 16.2.0.test.6 → 16.2.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/.prettierignore +3 -0
- data/.rubocop.yml +7 -90
- data/CHANGELOG.md +16 -7
- data/CONTRIBUTING.md +64 -43
- data/Gemfile.development_dependencies +4 -4
- data/Gemfile.loader +11 -8
- data/Gemfile.lock +147 -124
- 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 +71 -55
- 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 +57 -9
- 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 +55 -20
- 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
|
-
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
# Server-side rendering with code-splitting using Loadable/Components
|
|
2
|
+
|
|
2
3
|
by ShakaCode
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
_Last updated September 19, 2022_
|
|
5
6
|
|
|
6
7
|
## Introduction
|
|
8
|
+
|
|
7
9
|
The [React library recommends](https://loadable-components.com/docs/getting-started/) the use of React.lazy for code splitting with dynamic imports except
|
|
8
10
|
when using server-side rendering. In that case, as of February 2020, they recommend [Loadable Components](https://loadable-components.com)
|
|
9
|
-
for server-side rendering with dynamic imports.
|
|
11
|
+
for server-side rendering with dynamic imports.
|
|
10
12
|
|
|
11
13
|
Note, in 2019 and prior, the code-splitting feature was implemented using `react-loadable`. The React
|
|
12
14
|
team no longer recommends that library. The new way is far preferable.
|
|
@@ -18,7 +20,8 @@ yarn add @loadable/babel-plugin @loadable/component @loadable/server @loadable/
|
|
|
18
20
|
```
|
|
19
21
|
|
|
20
22
|
### Summary
|
|
21
|
-
|
|
23
|
+
|
|
24
|
+
- [`@loadable/babel-plugin`](https://loadable-components.com/docs/getting-started/) - The plugin transforms your code to be ready for Server Side Rendering.
|
|
22
25
|
- `@loadable/component` - Main library for creating loadable components.
|
|
23
26
|
- `@loadable/server` - Has functions for collecting chunks and provide style, script, link tags for the server.
|
|
24
27
|
- `@loadable/webpack-plugin` - The plugin to create a stats file with all chunks, assets information.
|
|
@@ -35,15 +38,16 @@ See example of server configuration differences in the loadable-components [exam
|
|
|
35
38
|
for server-side rendering](https://github.com/gregberge/loadable-components/blob/master/examples/server-side-rendering/webpack.config.babel.js)
|
|
36
39
|
|
|
37
40
|
You need to configure 3 things:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
|
|
42
|
+
1. `target`
|
|
43
|
+
a. client-side: `web`
|
|
44
|
+
b. server-side: `node`
|
|
41
45
|
2. `output.libraryTarget`
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
3. babel-loader options.caller = 'node' or 'web'
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
a. client-side: `undefined`
|
|
47
|
+
b. server-side: `commonjs2`
|
|
48
|
+
3. babel-loader options.caller = 'node' or 'web'
|
|
49
|
+
4. `plugins`
|
|
50
|
+
a. server-side: `new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 })`
|
|
47
51
|
|
|
48
52
|
```js
|
|
49
53
|
{
|
|
@@ -58,14 +62,15 @@ You need to configure 3 things:
|
|
|
58
62
|
Explanation:
|
|
59
63
|
|
|
60
64
|
- `target: 'node'` is required to be able to run the server bundle with the dynamic import logic on nodejs.
|
|
61
|
-
If that is not done, webpack will add and invoke browser-specific functions to fetch the chunks into the bundle, which throws an error on server-rendering.
|
|
65
|
+
If that is not done, webpack will add and invoke browser-specific functions to fetch the chunks into the bundle, which throws an error on server-rendering.
|
|
62
66
|
|
|
63
67
|
- `new webpack.optimize.LimitChunkCountPlugin({ maxChunks: 1 })`
|
|
64
|
-
The react_on_rails_pro node-renderer expects only one single server-bundle. In other words, we cannot and do not want to split the server bundle.
|
|
68
|
+
The react_on_rails_pro node-renderer expects only one single server-bundle. In other words, we cannot and do not want to split the server bundle.
|
|
65
69
|
|
|
66
70
|
#### Client config
|
|
67
71
|
|
|
68
72
|
For the client config we only need to add the plugin:
|
|
73
|
+
|
|
69
74
|
```js
|
|
70
75
|
{
|
|
71
76
|
plugins: [
|
|
@@ -74,30 +79,33 @@ For the client config we only need to add the plugin:
|
|
|
74
79
|
]
|
|
75
80
|
}
|
|
76
81
|
```
|
|
82
|
+
|
|
77
83
|
This plugin collects all the information about entrypoints, chunks, and files, that have these chunks and creates a stats file during client bundle build.
|
|
78
84
|
This stats file is used later to map rendered components to file assets. While you can use any filename, our documentation will use the default name.
|
|
79
85
|
|
|
80
86
|
### Babel
|
|
81
87
|
|
|
82
88
|
Per [the docs](https://loadable-components.com/docs/babel-plugin/#transformation):
|
|
89
|
+
|
|
83
90
|
> The plugin transforms your code to be ready for Server Side Rendering
|
|
84
91
|
|
|
85
92
|
Add this to `babel.config.js`:
|
|
93
|
+
|
|
86
94
|
```js
|
|
87
95
|
{
|
|
88
96
|
"plugins": ["@loadable/babel-plugin"]
|
|
89
97
|
}
|
|
90
98
|
```
|
|
91
|
-
https://loadable-components.com/docs/babel-plugin/
|
|
92
99
|
|
|
100
|
+
https://loadable-components.com/docs/babel-plugin/
|
|
93
101
|
|
|
94
102
|
### Convert components into loadable components
|
|
95
103
|
|
|
96
104
|
Instead of importing the component directly, use a dynamic import:
|
|
97
105
|
|
|
98
106
|
```js
|
|
99
|
-
import load from '@loadable/component'
|
|
100
|
-
const MyComponent = load(() => import('./MyComponent'))
|
|
107
|
+
import load from '@loadable/component';
|
|
108
|
+
const MyComponent = load(() => import('./MyComponent'));
|
|
101
109
|
```
|
|
102
110
|
|
|
103
111
|
### Resolving issue with ChunkLoadError
|
|
@@ -118,22 +126,25 @@ const consoleDebug = (fn) => {
|
|
|
118
126
|
console.debug(fn());
|
|
119
127
|
}
|
|
120
128
|
};
|
|
121
|
-
const retry = (fn, retryMessage = '', retriesLeft = 3, interval = 500) =>
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
129
|
+
const retry = (fn, retryMessage = '', retriesLeft = 3, interval = 500) =>
|
|
130
|
+
new Promise((resolve, reject) => {
|
|
131
|
+
fn()
|
|
132
|
+
.then(resolve)
|
|
133
|
+
.catch(() => {
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
if (retriesLeft === 1) {
|
|
136
|
+
console.warn(`Maximum retries exceeded, retryMessage: ${retryMessage}. Reloading page...`);
|
|
137
|
+
window.location.reload();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
// Passing on "reject" is the important part
|
|
141
|
+
consoleDebug(
|
|
142
|
+
() => `Trying request, retryMessage: ${retryMessage}, retriesLeft: ${retriesLeft - 1}`,
|
|
143
|
+
);
|
|
144
|
+
retry(fn, retryMessage, retriesLeft - 1, interval).then(resolve, reject);
|
|
145
|
+
}, interval);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
137
148
|
export default retry;
|
|
138
149
|
```
|
|
139
150
|
|
|
@@ -152,21 +163,21 @@ const HomePage = loadable(() => retry(() => import('./HomePage')));
|
|
|
152
163
|
|
|
153
164
|
In the client bundle, we need to wrap the `hydrateRoot` call into a `loadableReady` function.
|
|
154
165
|
So, hydration will be fired only after all necessary chunks preloads. In this example below,
|
|
155
|
-
`ClientApp` is registering as `App`.
|
|
166
|
+
`ClientApp` is registering as `App`.
|
|
156
167
|
|
|
157
168
|
```js
|
|
158
169
|
import React from 'react';
|
|
159
|
-
import ReactOnRails from 'react-on-rails';
|
|
160
|
-
import { hydrateRoot } from 'react-dom/client'
|
|
161
|
-
import { loadableReady } from '@loadable/component'
|
|
170
|
+
import ReactOnRails from 'react-on-rails-pro';
|
|
171
|
+
import { hydrateRoot } from 'react-dom/client';
|
|
172
|
+
import { loadableReady } from '@loadable/component';
|
|
162
173
|
import App from './App';
|
|
163
174
|
|
|
164
175
|
const ClientApp = (props, railsContext, domId) => {
|
|
165
176
|
loadableReady(() => {
|
|
166
|
-
const root = document.getElementById(domId)
|
|
177
|
+
const root = document.getElementById(domId);
|
|
167
178
|
hydrateRoot(root, <App {...props} />);
|
|
168
|
-
})
|
|
169
|
-
}
|
|
179
|
+
});
|
|
180
|
+
};
|
|
170
181
|
|
|
171
182
|
ReactOnRails.register({
|
|
172
183
|
App: ClientApp,
|
|
@@ -175,20 +186,20 @@ ReactOnRails.register({
|
|
|
175
186
|
|
|
176
187
|
#### Server
|
|
177
188
|
|
|
178
|
-
The purpose of the server function is to collect all rendered chunks and pass them as script, link,
|
|
179
|
-
style tags to the Rails view. In this example below, `ServerApp` is registering as `App`.
|
|
189
|
+
The purpose of the server function is to collect all rendered chunks and pass them as script, link,
|
|
190
|
+
style tags to the Rails view. In this example below, `ServerApp` is registering as `App`.
|
|
180
191
|
|
|
181
192
|
```js
|
|
182
193
|
import React from 'react';
|
|
183
|
-
import ReactOnRails from 'react-on-rails';
|
|
184
|
-
import { ChunkExtractor } from '@loadable/server'
|
|
185
|
-
import App from './App'
|
|
186
|
-
import path from 'path'
|
|
194
|
+
import ReactOnRails from 'react-on-rails-pro';
|
|
195
|
+
import { ChunkExtractor } from '@loadable/server';
|
|
196
|
+
import App from './App';
|
|
197
|
+
import path from 'path';
|
|
187
198
|
|
|
188
199
|
const ServerApp = (props, railsContext) => {
|
|
189
200
|
// This loadable-stats file was generated by `LoadablePlugin` in client webpack config.
|
|
190
201
|
// You must configure the path to resolve per your setup. If you are copying the file to
|
|
191
|
-
// a remote server, the file should be a sibling of this file.
|
|
202
|
+
// a remote server, the file should be a sibling of this file.
|
|
192
203
|
// __dirname is going to be the directory where the server-bundle.js exists
|
|
193
204
|
// Note, React on Rails Pro automatically copies the loadable-stats.json to the same place as the
|
|
194
205
|
// server-bundle.js. Thus, the __dirname of this code is where we can find loadable-stats.json.
|
|
@@ -198,10 +209,10 @@ const ServerApp = (props, railsContext) => {
|
|
|
198
209
|
// This object is used to search filenames by corresponding chunk names.
|
|
199
210
|
// See https://loadable-components.com/docs/api-loadable-server/#chunkextractor
|
|
200
211
|
// for the entryPoints, pass an array of all your entryPoints using dynamic imports
|
|
201
|
-
const extractor = new ChunkExtractor({ statsFile, entrypoints: ['client-bundle'] })
|
|
212
|
+
const extractor = new ChunkExtractor({ statsFile, entrypoints: ['client-bundle'] });
|
|
202
213
|
|
|
203
214
|
// It creates the wrapper `ChunkExtractorManager` around `App` to collect chunk names of rendered components.
|
|
204
|
-
const jsx = extractor.collectChunks(<App {...props} railsContext={railsContext} />)
|
|
215
|
+
const jsx = extractor.collectChunks(<App {...props} railsContext={railsContext} />);
|
|
205
216
|
|
|
206
217
|
const componentHtml = renderToString(jsx);
|
|
207
218
|
|
|
@@ -211,8 +222,8 @@ const ServerApp = (props, railsContext) => {
|
|
|
211
222
|
// Returns all the files with rendered chunks for furture insert into rails view.
|
|
212
223
|
linkTags: extractor.getLinkTags(),
|
|
213
224
|
styleTags: extractor.getStyleTags(),
|
|
214
|
-
scriptTags: extractor.getScriptTags()
|
|
215
|
-
}
|
|
225
|
+
scriptTags: extractor.getScriptTags(),
|
|
226
|
+
},
|
|
216
227
|
};
|
|
217
228
|
};
|
|
218
229
|
|
|
@@ -224,6 +235,7 @@ ReactOnRails.register({
|
|
|
224
235
|
## Configure react_on_rails_pro
|
|
225
236
|
|
|
226
237
|
### React on Rails Pro
|
|
238
|
+
|
|
227
239
|
You must set `config.assets_top_copy` so that the node-renderer will have access to the loadable-stats.json.
|
|
228
240
|
|
|
229
241
|
```ruby
|
|
@@ -233,15 +245,16 @@ You must set `config.assets_top_copy` so that the node-renderer will have access
|
|
|
233
245
|
Your server rendering code, per the above, will find this file like this:
|
|
234
246
|
|
|
235
247
|
```js
|
|
236
|
-
|
|
237
|
-
```
|
|
248
|
+
const statsFile = path.resolve(__dirname, 'loadable-stats.json');
|
|
249
|
+
```
|
|
238
250
|
|
|
239
251
|
Note, if `__dirname` is not working in your webpack build, that's because you didn't set `node: false`
|
|
240
252
|
in your webpack configuration. That turns off the polyfills for things like `__dirname`.
|
|
241
253
|
|
|
242
|
-
|
|
243
254
|
### Node Renderer
|
|
255
|
+
|
|
244
256
|
In your `node-renderer.js` file which runs node renderer, you need to specify `supportModules` options as follows:
|
|
257
|
+
|
|
245
258
|
```js
|
|
246
259
|
const path = require('path');
|
|
247
260
|
const env = process.env;
|
|
@@ -261,7 +274,7 @@ reactOnRailsProNodeRenderer(config);
|
|
|
261
274
|
```erb
|
|
262
275
|
<% res = react_component_hash("App", props: {}, prerender: true) %>
|
|
263
276
|
<%= content_for :link_tags, res['linkTags'] %>
|
|
264
|
-
<%= content_for :style_tags, res['styleTags'] %>
|
|
277
|
+
<%= content_for :style_tags, res['styleTags'] %>
|
|
265
278
|
|
|
266
279
|
<%= res['componentHtml'].html_safe %>
|
|
267
280
|
|
|
@@ -269,6 +282,7 @@ reactOnRailsProNodeRenderer(config);
|
|
|
269
282
|
```
|
|
270
283
|
|
|
271
284
|
## Making HMR Work
|
|
285
|
+
|
|
272
286
|
To make HMR work, it's best to disable loadable-components when using the Dev Server.
|
|
273
287
|
Note: you will need access to our **private** React on Rails Pro repository to open the following links.
|
|
274
288
|
|
|
@@ -277,9 +291,11 @@ Take a look at the code searches for ['imports-loadable'](https://github.com/sha
|
|
|
277
291
|
The general concept is that we have a non-loadable, HMR-ready, file that substitutes for the loadable-enabled one, with the suffixes `imports-hmr.js` instead of `imports-loadable.js`
|
|
278
292
|
|
|
279
293
|
### Webpack configuration
|
|
294
|
+
|
|
280
295
|
Use the [NormalModuleReplacement plugin](https://webpack.js.org/plugins/normal-module-replacement-plugin/):
|
|
281
296
|
|
|
282
297
|
[code](https://github.com/shakacode/react_on_rails_pro/blob/a361f4e163b9170f180ae07ee312fb9b4c719fc3/spec/dummy/config/webpack/environment.js#L81-L91)
|
|
298
|
+
|
|
283
299
|
```js
|
|
284
300
|
if (isWebpackDevServer) {
|
|
285
301
|
environment.plugins.append(
|
|
@@ -305,7 +321,7 @@ Note: you will need access to our **private** React on Rails Pro repository to o
|
|
|
305
321
|
### Client-Side Startup
|
|
306
322
|
|
|
307
323
|
- [spec/dummy/client/app/loadable/loadable-client.imports-hmr.js](https://github.com/shakacode/react_on_rails_pro/blob/master/spec/dummy/client/app/loadable/loadable-client.imports-hmr.js)
|
|
308
|
-
- [spec/dummy/client/app/loadable/loadable-client.imports-loadable.
|
|
324
|
+
- [spec/dummy/client/app/loadable/loadable-client.imports-loadable.jsx](https://github.com/shakacode/react_on_rails_pro/blob/master/spec/dummy/client/app/loadable/loadable-client.imports-loadable.jsx)
|
|
309
325
|
|
|
310
326
|
### Server-Side Startup
|
|
311
327
|
|