react_on_rails 11.0.5 → 13.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +338 -0
  3. data/.eslintignore +2 -1
  4. data/.eslintrc +32 -3
  5. data/.github/FUNDING.yml +1 -0
  6. data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
  7. data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  8. data/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  9. data/.github/workflows/lint-js-and-ruby.yml +54 -0
  10. data/.github/workflows/main.yml +183 -0
  11. data/.github/workflows/package-js-tests.yml +35 -0
  12. data/.github/workflows/rspec-package-specs.yml +46 -0
  13. data/.gitignore +3 -4
  14. data/.prettierignore +14 -0
  15. data/.prettierrc +20 -0
  16. data/.rubocop.yml +76 -34
  17. data/.travis.yml +15 -22
  18. data/CHANGELOG.md +443 -55
  19. data/CONTRIBUTING.md +62 -80
  20. data/Gemfile +1 -35
  21. data/Gemfile.development_dependencies +50 -0
  22. data/KUDOS.md +4 -1
  23. data/{docs/LICENSE.md → LICENSE.md} +1 -1
  24. data/NEWS.md +14 -4
  25. data/REACT-ON-RAILS-PRO-LICENSE +95 -0
  26. data/README.md +107 -802
  27. data/Rakefile +1 -8
  28. data/SUMMARY.md +51 -29
  29. data/book.json +5 -5
  30. data/docs/{basics/generator.md → additional-details/generator-details.md} +5 -13
  31. data/docs/{basics/installation-overview.md → additional-details/manual-installation-overview.md} +9 -14
  32. data/docs/{basics → additional-details}/migrating-from-react-rails.md +1 -1
  33. data/docs/additional-details/recommended-project-structure.md +69 -0
  34. data/docs/additional-details/tips-for-usage-with-sp6.md +15 -0
  35. data/docs/additional-details/upgrade-webpacker-v3-to-v4.md +10 -0
  36. data/docs/api/javascript-api.md +35 -6
  37. data/docs/api/redux-store-api.md +102 -0
  38. data/docs/api/view-helpers-api.md +133 -0
  39. data/docs/contributor-info/errors-with-hooks.md +45 -0
  40. data/docs/contributor-info/linters.md +5 -6
  41. data/docs/contributor-info/pull-requests.md +42 -0
  42. data/docs/contributor-info/releasing.md +1 -1
  43. data/docs/deployment/heroku-deployment.md +39 -0
  44. data/docs/getting-started.md +196 -0
  45. data/docs/guides/client-vs-server-rendering.md +27 -0
  46. data/docs/guides/configuration.md +289 -0
  47. data/docs/guides/deployment.md +5 -0
  48. data/docs/guides/file-system-based-automated-bundle-generation.md +197 -0
  49. data/docs/guides/hmr-and-hot-reloading-with-the-webpack-dev-server.md +104 -0
  50. data/docs/guides/how-react-on-rails-works.md +44 -0
  51. data/docs/guides/how-to-conditionally-server-render-based-on-device-type.md +40 -0
  52. data/docs/guides/how-to-use-different-files-for-client-and-server-rendering.md +98 -0
  53. data/docs/guides/i18n.md +87 -0
  54. data/docs/guides/installation-into-an-existing-rails-app.md +66 -0
  55. data/docs/guides/minitest-configuration.md +31 -0
  56. data/docs/guides/rails-webpacker-react-integration-options.md +213 -0
  57. data/docs/guides/react-on-rails-overview.md +29 -0
  58. data/docs/guides/react-server-rendering.md +32 -0
  59. data/docs/guides/render-functions-and-railscontext.md +205 -0
  60. data/docs/guides/rspec-configuration.md +73 -0
  61. data/docs/guides/tutorial.md +371 -0
  62. data/docs/{basics → guides}/upgrading-react-on-rails.md +126 -3
  63. data/docs/guides/webpack-configuration.md +42 -0
  64. data/docs/home.md +23 -0
  65. data/docs/javascript/asset-pipeline.md +12 -0
  66. data/docs/{additional-reading → javascript}/code-splitting.md +21 -11
  67. data/docs/javascript/converting-from-custom-webpack-config-to-rails-webpacker-config.md +10 -0
  68. data/docs/javascript/credits.md +10 -0
  69. data/docs/{additional-reading → javascript}/images.md +5 -6
  70. data/docs/javascript/react-helmet.md +100 -0
  71. data/docs/javascript/react-router.md +90 -0
  72. data/docs/{additional-reading → javascript}/server-rendering-tips.md +15 -12
  73. data/docs/javascript/troubleshooting-when-using-shakapacker.md +77 -0
  74. data/docs/{additional-reading → javascript}/webpack.md +2 -2
  75. data/docs/misc/articles.md +20 -0
  76. data/docs/misc/doctrine.md +5 -6
  77. data/docs/outdated/deferred-rendering.md +39 -0
  78. data/docs/{additional-reading → outdated}/rails-assets-relative-paths.md +4 -4
  79. data/docs/{additional-reading → outdated}/rails-assets.md +12 -20
  80. data/docs/{misc → outdated}/rails3.md +2 -2
  81. data/docs/rails/convert-rails-5-api-only-app.md +19 -0
  82. data/docs/rails/rails-engine-integration.md +32 -0
  83. data/docs/{additional-reading → rails}/rails_view_rendering_from_inline_javascript.md +2 -1
  84. data/docs/{additional-reading → rails}/turbolinks.md +13 -1
  85. data/docs/react-on-rails-pro/react-on-rails-pro.md +43 -0
  86. data/docs/testimonials/hvmn.md +25 -0
  87. data/docs/testimonials/resortpass.md +13 -0
  88. data/docs/testimonials/testimonials.md +28 -0
  89. data/jest.config.js +4 -0
  90. data/lib/generators/USAGE +1 -1
  91. data/lib/generators/react_on_rails/adapt_for_older_shakapacker_generator.rb +41 -0
  92. data/lib/generators/react_on_rails/base_generator.rb +55 -43
  93. data/lib/generators/react_on_rails/bin/dev +30 -0
  94. data/lib/generators/react_on_rails/bin/dev-static +30 -0
  95. data/lib/generators/react_on_rails/dev_tests_generator.rb +4 -3
  96. data/lib/generators/react_on_rails/generator_helper.rb +8 -6
  97. data/lib/generators/react_on_rails/generator_messages.rb +40 -0
  98. data/lib/generators/react_on_rails/install_generator.rb +37 -0
  99. data/lib/generators/react_on_rails/templates/.eslintrc +3 -1
  100. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev +4 -6
  101. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-static +9 -0
  102. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +21 -40
  103. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorld.module.css +4 -0
  104. data/lib/generators/react_on_rails/templates/base/base/app/javascript/bundles/HelloWorld/components/HelloWorldServer.js +5 -0
  105. data/lib/generators/react_on_rails/templates/base/base/app/javascript/packs/server-bundle.js +8 -0
  106. data/lib/generators/react_on_rails/templates/base/base/app/views/layouts/hello_world.html.erb +2 -1
  107. data/lib/generators/react_on_rails/templates/base/base/babel.config.js.tt +32 -0
  108. data/lib/generators/react_on_rails/templates/base/base/config/initializers/react_on_rails.rb +20 -4
  109. data/lib/generators/react_on_rails/templates/base/base/config/shakapacker.yml +62 -0
  110. data/lib/generators/react_on_rails/templates/base/base/config/webpack/clientWebpackConfig.js.tt +17 -0
  111. data/lib/generators/react_on_rails/templates/base/base/config/webpack/commonWebpackConfig.js.tt +17 -0
  112. data/lib/generators/react_on_rails/templates/base/base/config/webpack/development.js.tt +25 -0
  113. data/lib/generators/react_on_rails/templates/base/base/config/webpack/production.js.tt +9 -0
  114. data/lib/generators/react_on_rails/templates/base/base/config/webpack/serverWebpackConfig.js.tt +117 -0
  115. data/lib/generators/react_on_rails/templates/base/base/config/webpack/test.js.tt +9 -0
  116. data/lib/generators/react_on_rails/templates/base/base/config/webpack/webpack.config.js.tt +15 -0
  117. data/lib/generators/react_on_rails/templates/base/base/config/webpack/webpackConfig.js.tt +36 -0
  118. data/lib/generators/react_on_rails/templates/dev_tests/spec/rails_helper.rb +8 -2
  119. data/lib/generators/react_on_rails/templates/dev_tests/spec/simplecov_helper.rb +1 -1
  120. data/lib/generators/react_on_rails/templates/dev_tests/spec/{features → system}/hello_world_spec.rb +2 -2
  121. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/components/HelloWorld.jsx +6 -9
  122. data/lib/generators/react_on_rails/templates/redux/base/app/javascript/bundles/HelloWorld/store/helloWorldStore.js +1 -3
  123. data/lib/react_on_rails/configuration.rb +198 -145
  124. data/lib/react_on_rails/error.rb +2 -0
  125. data/lib/react_on_rails/git_utils.rb +5 -3
  126. data/lib/react_on_rails/{react_on_rails_helper.rb → helper.rb} +201 -190
  127. data/lib/react_on_rails/json_output.rb +1 -1
  128. data/lib/react_on_rails/json_parse_error.rb +28 -0
  129. data/lib/react_on_rails/locales/base.rb +169 -0
  130. data/lib/react_on_rails/locales/to_js.rb +33 -0
  131. data/lib/react_on_rails/locales/to_json.rb +23 -0
  132. data/lib/react_on_rails/packs_generator.rb +234 -0
  133. data/lib/react_on_rails/prerender_error.rb +35 -27
  134. data/lib/react_on_rails/react_component/render_options.rb +64 -9
  135. data/lib/react_on_rails/server_rendering_js_code.rb +55 -0
  136. data/lib/react_on_rails/server_rendering_pool/ruby_embedded_java_script.rb +126 -76
  137. data/lib/react_on_rails/server_rendering_pool.rb +0 -1
  138. data/lib/react_on_rails/test_helper/ensure_assets_compiled.rb +9 -8
  139. data/lib/react_on_rails/test_helper/webpack_assets_compiler.rb +17 -0
  140. data/lib/react_on_rails/test_helper/webpack_assets_status_checker.rb +13 -12
  141. data/lib/react_on_rails/test_helper.rb +24 -3
  142. data/lib/react_on_rails/utils.rb +94 -25
  143. data/lib/react_on_rails/version.rb +1 -1
  144. data/lib/react_on_rails/version_checker.rb +5 -1
  145. data/lib/react_on_rails/version_syntax_converter.rb +14 -12
  146. data/lib/react_on_rails/webpacker_utils.rb +105 -5
  147. data/lib/react_on_rails.rb +8 -2
  148. data/lib/tasks/assets.rake +28 -60
  149. data/lib/tasks/generate_packs.rake +11 -0
  150. data/lib/tasks/locale.rake +5 -4
  151. data/package-scripts.yml +49 -0
  152. data/package.json +52 -47
  153. data/rakelib/docker.rake +0 -5
  154. data/rakelib/dummy_apps.rake +5 -8
  155. data/rakelib/example_type.rb +12 -3
  156. data/rakelib/examples.rake +5 -4
  157. data/rakelib/lint.rake +5 -16
  158. data/rakelib/node_package.rake +2 -2
  159. data/rakelib/release.rake +37 -23
  160. data/rakelib/run_rspec.rake +16 -44
  161. data/rakelib/task_helpers.rb +16 -4
  162. data/react_on_rails.gemspec +6 -22
  163. data/tsconfig.json +14 -0
  164. data/webpackConfigLoader.js +5 -4
  165. data/yarn.lock +5935 -3106
  166. metadata +122 -272
  167. data/Gemfile.rails32 +0 -74
  168. data/docs/additional-reading/asset-pipeline.md +0 -20
  169. data/docs/additional-reading/babel.md +0 -5
  170. data/docs/additional-reading/caching-and-performance.md +0 -4
  171. data/docs/additional-reading/heroku-deployment.md +0 -92
  172. data/docs/additional-reading/hot-reloading-rails-development.md +0 -57
  173. data/docs/additional-reading/node-server-rendering.md +0 -5
  174. data/docs/additional-reading/rails-engine-integration.md +0 -34
  175. data/docs/additional-reading/react-helmet.md +0 -80
  176. data/docs/additional-reading/react-router.md +0 -113
  177. data/docs/additional-reading/recommended-project-structure.md +0 -49
  178. data/docs/additional-reading/rspec-configuration.md +0 -56
  179. data/docs/additional-reading/webpack-dev-server.md +0 -15
  180. data/docs/api/ruby-api-hot-reload-view-helpers.md +0 -44
  181. data/docs/api/ruby-api.md +0 -8
  182. data/docs/basics/configuration.md +0 -163
  183. data/docs/basics/i18n.md +0 -77
  184. data/docs/tutorial.md +0 -220
  185. data/lib/generators/react_on_rails/templates/base/base/Procfile.dev-server +0 -12
  186. data/lib/react_on_rails/assets_precompile.rb +0 -150
  187. data/lib/react_on_rails/locales_to_js.rb +0 -134
  188. data/ruby-lint.yml +0 -25
  189. /data/docs/{additional-reading → additional-details}/updating-dependencies.md +0 -0
  190. /data/docs/{additional-reading → deployment}/elastic-beanstalk.md +0 -0
  191. /data/docs/{additional-reading → javascript}/angular-js-integration-migration.md +0 -0
  192. /data/docs/{additional-reading → javascript}/capistrano-deployment.md +0 -0
  193. /data/docs/{additional-reading → javascript}/foreman-issues.md +0 -0
  194. /data/docs/{additional-reading → javascript}/node-dependencies-and-npm.md +0 -0
  195. /data/docs/{additional-reading → javascript}/react-and-redux.md +0 -0
  196. /data/docs/{additional-reading → javascript}/troubleshooting-when-using-webpacker.md +0 -0
  197. /data/docs/{additional-reading → javascript}/webpack-v1-notes.md +0 -0
  198. /data/docs/{coding-style → misc}/style.md +0 -0
  199. /data/docs/{additional-reading → misc}/tips.md +0 -0
@@ -0,0 +1,40 @@
1
+ # How to conditionally render server side based on the device type
2
+
3
+ In general, we want to use CSS to do mobile responsive layouts.
4
+
5
+ However, sometimes we want to have layouts sufficiently different that we can't do this via CSS. If we didn't do server rendering, we can check the device type on the client side. However, if we're doing server rendering, we need to send this data to the client code from the Rails server so that the server rendering can account for this.
6
+
7
+ Here's an example:
8
+
9
+ ## config/initializers/react_on_rails.rb
10
+
11
+ ```ruby
12
+ module RenderingExtension
13
+ # Return a Hash that contains custom values from the view context that will get passed to
14
+ # all calls to react_component and redux_store for rendering
15
+ def self.custom_context(view_context)
16
+ if view_context.controller.is_a?(ActionMailer::Base)
17
+ {}
18
+ else
19
+ {
20
+ desktop: !(view_context.browser.device.tablet? || view_context.browser.device.mobile?),
21
+ tablet: view_context.browser.device.tablet?,
22
+ mobile: view_context.browser.device.mobile? || false
23
+ }
24
+ end
25
+ end
26
+ end
27
+
28
+ # Shown below are the defaults for configuration
29
+ ReactOnRails.configure do |config|
30
+ # See https://github.com/shakacode/react_on_rails/blob/master/docs/guides/configuration.md for the rest
31
+
32
+ # This allows you to add additional values to the Rails Context. Implement one static method
33
+ # called `custom_context(view_context)` and return a Hash.
34
+ config.rendering_extension = RenderingExtension
35
+ end
36
+ ```
37
+
38
+ Note, full details of the React on Rails configuration are [here in docs/basics/configuration.md](https://shakacode.com/react-on-rails/docs/guides/configuration/).
39
+
40
+ See the doc file [docs/basics/generator-functions-and-railscontext.md](https://shakacode.com/react-on-rails/docs/guides/generator-functions-and-railscontext/#rails-context) for how your client-side code uses the device information
@@ -0,0 +1,98 @@
1
+ ## How to use different versions of a file for client and server rendering
2
+
3
+ There are 3 main ways to use different code for server vs. client rendering.
4
+
5
+ ## A. Using different Entry Points
6
+ Many projects will have different entry points for client and server rendering. This only works for a top-level entry point such as the entry point for a react-router app component.
7
+
8
+ Your Client Entry can look like this:
9
+
10
+ ```js
11
+ import ReactOnRails from 'react-on-rails';
12
+ import App from './ClientApp';
13
+ ReactOnRails.register({ App })
14
+ ```
15
+
16
+ So your Server Entry can look like:
17
+
18
+ ```js
19
+ import ReactOnRails from 'react-on-rails';
20
+ import App from './ServerApp';
21
+ ReactOnRails.register({ App })
22
+ ```
23
+
24
+ Note that the only difference is on the second line of each of these examples.
25
+
26
+ ## B. Two Options for Using Webpack Resolve Alias in the Webpack Config
27
+ Per [Webpack Docs](https://webpack.js.org/configuration/resolve/#resolve-alias).
28
+
29
+ ### 1. Update `webpack/set-resolve.js` to have a different resolution for the exact file:
30
+
31
+ ```js
32
+ function setResolve(builderConfig, webpackConfig) {
33
+
34
+ // Use a different resolution for Client and Server file
35
+ let SomeJsFile;
36
+ if (builderConfig.serverRendering) {
37
+ SomeJsFile = path.resolve(__dirname, "../bundles/SomeJsFileServer");
38
+ } else {
39
+ SomeJsFile = path.resolve(__dirname, "../bundles/SomeJsFileClient");
40
+ }
41
+
42
+ const resolve = {
43
+ alias: {
44
+ ... // blah blah
45
+ SomeJsFile,
46
+ ... // blah blah
47
+ },
48
+ ```
49
+
50
+ Then you have this import:
51
+
52
+ ```js
53
+ import SomeJsFile from 'SomeJsFile';
54
+ ```
55
+
56
+ ### 2. Use a different resolution for the right directory of client or server files:
57
+
58
+ #### a. Update `webpack/set-resolve.js` to have something like:
59
+ ```js
60
+ function setResolve(builderConfig, webpackConfig) {
61
+
62
+ // Use a different resolution for Client and Server file
63
+ let variant;
64
+ if (builderConfig.serverRendering) {
65
+ variant = path.resolve(__dirname, "../bundles/variant/ClientOnly");
66
+ } else {
67
+ variant = path.resolve(__dirname, "../bundles/variant/serverOnly");
68
+ }
69
+
70
+ const resolve = {
71
+ alias: {
72
+ ... // blah blah
73
+ variant
74
+ ... // blah blah
75
+ },
76
+ ```
77
+
78
+ #### b. Add different versions of the file to the `bundles/variant/ClientOnly` and `bundles/variant/ServerOnly` directories
79
+
80
+ #### c. Use the `variant` in import in a file that can be used both for client and server rendering:
81
+
82
+ ```js
83
+ import SomeJsFile from 'variant/SomeJsFile';
84
+ import AnotherJsFile from 'variant/AnotherJsFile';
85
+ ```
86
+
87
+ ## C. Conditional code that can check if `window` is defined.
88
+
89
+ This is probably the ugliest and hackiest way to do this, but it's quick! Essentially you wrap code that cannot execute server side in a conditional:
90
+
91
+ ```js
92
+ if (window) { // window should be falsy on the server side
93
+ doSomethingClientOnly();
94
+
95
+ // or do an import
96
+ const foobar = require('foobar').default;
97
+ }
98
+ ```
@@ -0,0 +1,87 @@
1
+ # Internationalization
2
+
3
+ You can use [Rails internationalization (i18n)](https://guides.rubyonrails.org/i18n.html) in your client code.
4
+
5
+ 1. Set `config.i18n_dir` in `config/initializers/react_on_rails.rb`:
6
+
7
+ ```ruby
8
+ # Replace the following line by the directory containing your translation.js and default.js files.
9
+ config.i18n_dir = Rails.root.join("PATH_TO", "YOUR_JS_I18N_FOLDER")
10
+ ```
11
+
12
+ If you do not want to use the YAML files from `Rails.root.join("config", "locales")` and installed gems, you can also set `config.i18n_yml_dir`:
13
+ ```ruby
14
+ # Replace the following line by the location of your client i18n yml files
15
+ # Without this option, all YAML files from Rails.root.join("config", "locales") and installed gems are loaded
16
+ config.i18n_yml_dir = Rails.root.join("PATH_TO", "YOUR_YAML_I18N_FOLDER")
17
+ ```
18
+
19
+ 2. Add that directory (or just the generated files `translations.json` and `default.json`) to your `.gitignore`.
20
+
21
+ 3. The locale files must be generated before `yarn build` using `rake react_on_rails:locale`.
22
+
23
+ For development, you should adjust your startup scripts (`Procfile`s) so that they run `bundle exec rake react_on_rails:locale` before running any webpack watch process (`yarn run build:development`).
24
+
25
+ If you are not using the React on Rails test helper,
26
+ you may need to configure your CI to run `bundle exec rake react_on_rails:locale` before any webpack process as well.
27
+
28
+ Note: if you try to lint before running tests, and you depend on the test helper to build your locales, linting will fail because the translations won't be built yet.
29
+
30
+ The fix is either to
31
+ 1) run the rake task to build the translations before running the lint command or
32
+ 2) to run the tests first.
33
+
34
+ By default, the locales are generated as JSON, but you can also generate them as JavaScript with [`react-intl`](https://formatjs.io/docs/getting-started/installation/) support:
35
+
36
+ 1. Specify the i18n output format in `config/initializers/react_on_rails.rb`:
37
+ ```rb
38
+ config.i18n_output_format = "js"
39
+ ```
40
+
41
+ 2. Add `react-intl` & `intl` to `client/package.json`, and remember to `bundle install && yarn install`. The minimum supported versions are:
42
+
43
+ ```js
44
+ "dependencies": {
45
+ ...
46
+ "intl": "^1.2.5",
47
+ "react-intl": "^2.1.5",
48
+ ...
49
+ }
50
+ ```
51
+
52
+ 3. In React, you need to initialize `react-intl`, and set its parameters:
53
+
54
+ ```js
55
+ ...
56
+ import { addLocaleData } from 'react-intl';
57
+ import en from 'react-intl/locale-data/en';
58
+ import de from 'react-intl/locale-data/de';
59
+ import { translations } from 'path_to/i18n/translations';
60
+ import { defaultLocale } from 'path_to/i18n/default';
61
+ ...
62
+ // Initizalize all locales for react-intl.
63
+ addLocaleData([...en, ...de]);
64
+ ...
65
+ // set locale and messages for IntlProvider.
66
+ const locale = method_to_get_current_locale() || defaultLocale;
67
+ const messages = translations[locale];
68
+ ...
69
+ return (
70
+ <IntlProvider locale={locale} key={locale} messages={messages}>
71
+ <CommentScreen {...{ actions, data }} />
72
+ </IntlProvider>
73
+ )
74
+ ```
75
+ ```js
76
+ // In your component.
77
+ import { defaultMessages } from 'path_to/i18n/default';
78
+ ...
79
+ return (
80
+ { formatMessage(defaultMessages.yourLocaleKeyInCamelCase) }
81
+ )
82
+ ```
83
+
84
+ # Notes
85
+ * See why using JSON can perform better compared to JS for large amounts of data [https://v8.dev/blog/cost-of-javascript-2019#json](https://v8.dev/blog/cost-of-javascript-2019#json).
86
+ * See [Support for Rails' i18n pluralization #1000](https://github.com/shakacode/react_on_rails/issues/1000) for a discussion of issues around pluralization.
87
+ * *Outdated:* You can refer to [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial) and [PR #340](https://github.com/shakacode/react-webpack-rails-tutorial/pull/340), [commmited](https://github.com/shakacode/react-webpack-rails-tutorial/commit/ef369ed9d922aea5116ca7e50208169fd7831389) for a complete example.
@@ -0,0 +1,66 @@
1
+ # Getting Started with an existing Rails app
2
+
3
+ **Also consult the instructions for installing on a fresh Rails app**, see the [React on Rails Basic Tutorial](https://www.shakacode.com/react-on-rails/docs/guides/tutorial/).
4
+
5
+ **If you have rails-5 API only project**, first [convert the rails-5 API only app to rails app](https://www.shakacode.com/react-on-rails/docs/rails/convert-rails-5-api-only-app-to-rails-app/).
6
+
7
+ 1. Add the following to your Gemfile and `bundle install`. We recommend fixing the version of React on Rails, as you will need to keep the exact version in sync with the version in your `package.json` file.
8
+
9
+ ```ruby
10
+ gem "shakapacker", "7.0.1" # Use the latest and the exact version
11
+ gem "react_on_rails", "13.3.1" # Use the latest and the exact version
12
+ ```
13
+
14
+ Or use `bundle add`:
15
+
16
+ ```bash
17
+ bundle add shakapacker --version=7.0.1 --strict
18
+ bundle add react_on_rails --version=13.3.1 --strict
19
+ ```
20
+
21
+ 2. Run the following 2 commands to install Shakapacker with React. Note, if you are using an older version of Rails than 5.1, you'll need to install Webpacker with React per the instructions [here](https://github.com/rails/webpacker).
22
+
23
+ ```bash
24
+ rails shakapacker:install
25
+ ```
26
+
27
+ 3. Commit this to git (or else you cannot run the generator unless you pass the option `--ignore-warnings`).
28
+
29
+ 4. Run the generator with a simple "Hello World" example (more options below):
30
+
31
+ ```bash
32
+ rails generate react_on_rails:install
33
+ ```
34
+
35
+ For more information about this generator use `--help` option:
36
+
37
+ ```bash
38
+ rails generate react_on_rails:install --help
39
+ ```
40
+ 5. Ensure that you have `overmind` or `foreman` installed.
41
+
42
+ Note: `foreman` should be installed on the system not on your project. [Read more](https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman)
43
+
44
+ 6. Start your Rails server:
45
+
46
+ ```bash
47
+ ./bin/dev
48
+ ```
49
+ Note: `foreman` defaults to PORT 5000 unless you set the value of PORT in your environment. For example, you can `export PORT=3000` to use the Rails default port of 3000. For the hello_world example, this is already set.
50
+
51
+ 7. Visit [localhost:3000/hello_world](http://localhost:3000/hello_world).
52
+
53
+ ## Installation
54
+
55
+ See the [Installation Overview](https://www.shakacode.com/react-on-rails/docs/additional-details/manual-installation-overview/) for a concise set summary of what's in a React on Rails installation.
56
+
57
+
58
+ ## NPM
59
+
60
+ All JavaScript in React On Rails is loaded from npm: [react-on-rails](https://www.npmjs.com/package/react-on-rails). To manually install this (you did not use the generator), assuming you have a standard configuration, run this command (assuming you are in the directory where you have your `node_modules`):
61
+
62
+ ```bash
63
+ yarn add react-on-rails --exact
64
+ ```
65
+
66
+ That will install the latest version and update your package.json. **NOTE:** the `--exact` flag will ensure that you do not have a "~" or "^" for your react-on-rails version in your package.json.
@@ -0,0 +1,31 @@
1
+ # Minitest Configuration
2
+
3
+ The setup for minitest is the same as for rspec with the following difference.
4
+
5
+ Rather than calling `ReactOnRails::TestHelper.configure_rspec_to_compile_assets(config)`, instead you will do something like this:
6
+
7
+ ```ruby
8
+ class ActiveSupport::TestCase
9
+ setup do
10
+ ReactOnRails::TestHelper.ensure_assets_compiled
11
+ end
12
+ end
13
+ ```
14
+
15
+
16
+ Or maybe something like this, from the [minitest docs](https://github.com/seattlerb/minitest/blob/master/lib/minitest/test.rb#L119):
17
+
18
+ ```ruby
19
+ module MyMinitestPlugin
20
+ def before_setup
21
+ super
22
+ ReactOnRails::TestHelper.ensure_assets_compiled
23
+ end
24
+ end
25
+
26
+ class MiniTest::Test
27
+ include MyMinitestPlugin
28
+ end
29
+ ```
30
+
31
+
@@ -0,0 +1,213 @@
1
+ # Shakapacker (Rails/Webpacker) React Integration Options
2
+
3
+ You only _need_ props hydration if you need SSR. However, there's no good reason to
4
+ have your app make a second round trip to the Rails server to get initialization props.
5
+
6
+ **Server-Side Rendering (SSR)** results in Rails rendering HTML for your React components. The main reasons to use SSR are better SEO and pages display more quickly.
7
+
8
+ These gems provide advanced integration of React with [shakacode/shakapacker](https://github.com/shakacode/shakapacker):
9
+
10
+ | Gem | Props Hydration | Server-Side-Rendering (SSR) | SSR with HMR | SSR with React-Router | SSR with Code Splitting | Node SSR |
11
+ | --- | --------------- | --- | --------------------- | ----------------------| ------------------------|----|
12
+ | [shakacode/react_on_rails](https://github.com/shakacode/react_on_rails) | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
13
+ | [react-rails](https://github.com/reactjs/react-rails) | ✅ | ✅ | | | | | |
14
+ | [webpacker-react](https://github.com/renchap/webpacker-react) | ✅ | | | | | | |
15
+
16
+ Note, Node SSR for React on Rails requires [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/).
17
+
18
+ ---
19
+
20
+ As mentioned, you don't _need_ to use a gem to integrate Rails with React.
21
+
22
+ If you're not concerned with view helpers to pass props or server rendering, you can do it yourself:
23
+
24
+ ```erb
25
+ <%# views/layouts/application.html.erb %>
26
+
27
+ <%= content_tag :div,
28
+ id: "hello-react",
29
+ data: {
30
+ message: 'Hello!',
31
+ name: 'David'
32
+ }.to_json do %>
33
+ <% end %>
34
+ ```
35
+
36
+ ```js
37
+ // app/javascript/packs/hello_react.js
38
+
39
+ const Hello = props => (
40
+ <div className='react-app-wrapper'>
41
+ <img src={clockIcon} alt="clock" />
42
+ <h5 className='hello-react'>
43
+ {props.message} {props.name}!
44
+ </h5>
45
+ </div>
46
+ )
47
+
48
+ // Render component with data
49
+ document.addEventListener('DOMContentLoaded', () => {
50
+ const node = document.getElementById('hello-react')
51
+ const data = JSON.parse(node.getAttribute('data'))
52
+
53
+ ReactDOM.render(<Hello {...data} />, node)
54
+ })
55
+ ```
56
+
57
+ ----
58
+
59
+ ## Suppress warning related to Can't resolve 'react-dom/client' in React < 18
60
+
61
+ You may see a warning like this when building a Webpack bundle using any version of React below 18:
62
+
63
+ ```
64
+ Module not found: Error: Can't resolve 'react-dom/client' in ....
65
+ ```
66
+
67
+ It can be safely [suppressed](https://webpack.js.org/configuration/other-options/#ignorewarnings) in your Webpack configuration. The following is an example of this suppression in `config/webpack/commonWebpackConfig.js`:
68
+
69
+ ```js
70
+ const { webpackConfig: baseClientWebpackConfig, merge } = require('shakapacker');
71
+
72
+ const commonOptions = {
73
+ resolve: {
74
+ extensions: ['.css', '.ts', '.tsx'],
75
+ },
76
+ };
77
+
78
+ const ignoreWarningsConfig = {
79
+ ignoreWarnings: [/Module not found: Error: Can't resolve 'react-dom\/client'/],
80
+ };
81
+
82
+ const commonWebpackConfig = () => merge({}, baseClientWebpackConfig, commonOptions, ignoreWarningsConfig);
83
+
84
+ module.exports = commonWebpackConfig;
85
+ ```
86
+
87
+ ----
88
+
89
+ ## HMR and React Hot Reloading
90
+
91
+ Before turning HMR on, consider upgrading to the latest stable gems and packages:
92
+ https://github.com/shakacode/shakapacker#upgrading
93
+
94
+ Configure `config/shakapacker.yml` file:
95
+
96
+ ```yaml
97
+ development:
98
+ extract_css: false
99
+ dev_server:
100
+ hmr: true
101
+ inline: true
102
+ ```
103
+
104
+ This basic configuration alone will have HMR working with the default Shakapacker setup. However, a code save will trigger a full page refresh each time you save a file.
105
+
106
+ Webpack's HMR allows the replacement of modules for React in-place without reloading the browser. To do this, you have two options:
107
+
108
+ 1. Steps below for the [github.com/pmmmwh/react-refresh-webpack-plugin](https://github.com/pmmmwh/react-refresh-webpack-plugin).
109
+ 1. Deprecated steps below for using the [github.com/gaearon/react-hot-loader](https://github.com/gaearon/react-hot-loader).
110
+
111
+ ### React Refresh Webpack Plugin
112
+ [github.com/pmmmwh/react-refresh-webpack-plugin](https://github.com/pmmmwh/react-refresh-webpack-plugin)
113
+
114
+ You can see an example commit of adding this [here](https://github.com/shakacode/react_on_rails_demo_ssr_hmr/commit/7e53803fce7034f5ecff335db1f400a5743a87e7).
115
+
116
+ 1. Add react refresh packages:
117
+ `yarn add @pmmmwh/react-refresh-webpack-plugin react-refresh -D`
118
+ 2. Update `babel.config.js` adding
119
+ ```js
120
+ plugins: [
121
+ process.env.WEBPACK_DEV_SERVER && 'react-refresh/babel',
122
+ // other plugins
123
+ ```
124
+ 3. Update `config/webpack/development.js`, only including the plugin if running the WEBPACK_DEV_SERVER
125
+ ```js
126
+ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
127
+ const environment = require('./environment')
128
+
129
+ const isWebpackDevServer = process.env.WEBPACK_DEV_SERVER;
130
+
131
+ //plugins
132
+ if (isWebpackDevServer) {
133
+ environment.plugins.append(
134
+ 'ReactRefreshWebpackPlugin',
135
+ new ReactRefreshWebpackPlugin({
136
+ overlay: {
137
+ sockPort: 3035
138
+ }
139
+ })
140
+ );
141
+ }
142
+ ```
143
+
144
+ ---
145
+
146
+ ### React Hot Loader (Deprecated)
147
+
148
+ 1. Add the `react-hot-loader` and ` @hot-loader/react-dom` npm packages.
149
+ ```sh
150
+ yarn add --dev react-hot-loader @hot-loader/react-dom
151
+ ```
152
+
153
+ 2. Update your babel config, `babel.config.js`. Add the plugin `react-hot-loader/babel`
154
+ with option `"safetyNet": false`:
155
+
156
+ ```
157
+ {
158
+ "plugins": [
159
+ [
160
+ "react-hot-loader/babel",
161
+ {
162
+ "safetyNet": false
163
+ }
164
+ ]
165
+ ]
166
+ }
167
+ ```
168
+
169
+ 3. Add changes like this to your entry points:
170
+
171
+ ```diff
172
+ // app/javascript/app.jsx
173
+
174
+ import React from 'react';
175
+ + import { hot } from 'react-hot-loader/root';
176
+
177
+ const App = () => <SomeComponent(s) />
178
+
179
+ - export default App;
180
+ + export default hot(App);
181
+ ```
182
+
183
+ 4. Adjust your webpack configuration for development so that `sourceMapContents` option for the sass
184
+ loader is `false`:
185
+
186
+ ```diff
187
+ // config/webpack/development.js
188
+
189
+ process.env.NODE_ENV = process.env.NODE_ENV || 'development'
190
+
191
+ const environment = require('./environment')
192
+
193
+ // allows for editing sass/scss files directly in browser
194
+ + if (!module.hot) {
195
+ + environment.loaders.get('sass').use.find(item => item.loader === 'sass-loader').options.sourceMapContents = false
196
+ + }
197
+ +
198
+ module.exports = environment.toWebpackConfig()
199
+ ```
200
+
201
+ 5. Adjust your `config/webpack/environment.js` for a
202
+
203
+ ```diff
204
+ // config/webpack/environment.js
205
+
206
+ // ...
207
+
208
+ // Fixes: React-Hot-Loader: react-🔥-dom patch is not detected. React 16.6+ features may not work.
209
+ // https://github.com/gaearon/react-hot-loader/issues/1227#issuecomment-482139583
210
+ + environment.config.merge({ resolve: { alias: { 'react-dom': '@hot-loader/react-dom' } } });
211
+
212
+ module.exports = environment;
213
+ ```
@@ -0,0 +1,29 @@
1
+ # React on Rails
2
+
3
+ React on Rails integrates Rails with (server rendering of) Facebook's [React](https://github.com/facebook/react) front-end framework.
4
+
5
+ ---
6
+
7
+ # Project Objective
8
+
9
+ To provide a high performance framework for integrating Ruby on Rails with React via the [**Shakapacker**](https://github.com/shakacode/shakapacker) gem especially in regards to React Server-Side Rendering for better SEO and improved performance.
10
+
11
+ # Features and Why React on Rails?
12
+
13
+ 1. Easy passing of props directly from your Rails view to your React components rather than having your Rails view load and then make a separate request to your API.
14
+ 1. Tight integration with [shakacode/shakapacker](https://github.com/shakacode/shakapacker).
15
+ 1. Server-Side Rendering (SSR), often used for SEO crawler indexing and UX performance, is not offered by `shakacode/shakapacker`.
16
+ 1. Support for HMR for a great developer experience.
17
+ 1. Supports latest versions of React with hooks.
18
+ 1. [Redux](https://github.com/reactjs/redux) and [React Router](https://github.com/ReactTraining/react-router#readme) integration including server-side-rendering.
19
+ 1. [Internationalization (I18n) and (localization)](https://www.shakacode.com/react-on-rails/docs/guides/i18n/)
20
+ 1. A supportive community. This [web search shows how live public sites are using React on Rails](https://publicwww.com/websites/%22react-on-rails%22++-undeveloped.com+depth%3Aall/).
21
+ 1. [ReScript (Reason ML) Support](https://github.com/shakacode/reason-react-on-rails-example).
22
+
23
+ See the [react-webpack-rails-tutorial](https://github.com/shakacode/react-webpack-rails-tutorial) for an example of a live implementation and code.
24
+
25
+ ----
26
+
27
+ ## Prerequisites
28
+ - Ruby on Rails >=5
29
+ - Shakapacker 6.5.1+.
@@ -0,0 +1,32 @@
1
+ # React Server Rendering
2
+
3
+ See also [Client vs. Server Rendering](https://www.shakacode.com/react-on-rails/docs/guides/client-vs-server-rendering/).
4
+
5
+ ## What is the easiest way to setup a webpack configuration for server-side-rendering?
6
+ See the example webpack setup here: [github.com/shakacode/react_on_rails_demo_ssr_hmr](https://github.com/shakacode/react_on_rails_demo_ssr_hmr).
7
+
8
+ ## What is Server Rendering?
9
+
10
+ Here's a [decent article to introduce you to server rendering](https://medium.freecodecamp.org/server-side-rendering-your-react-app-in-three-simple-steps-7a82b95db82e). Note, React on Rails takes care of calling the methods in [ReactDOMServer](https://reactjs.org/docs/react-dom-server.html).
11
+
12
+ During the Rails rendering of HTML per a browser request, the Rails server will execute some JavaScript to create a string of HTML used for React server rendering. This resulting HTML is placed with in your Rails view's output.
13
+
14
+ The default JavaScript interpretter is [ExecJS](https://github.com/rails/execjs). If you want to maximize the perfomance of your server rendering, then you want to use React on Rails Pro which uses NodeJS to do the server rendering. See the [docs for React on Rails Pro](https://github.com/shakacode/react_on_rails/wiki).
15
+
16
+ See [this note](https://www.shakacode.com/react-on-rails/docs/guides/client-vs-server-rendering/).
17
+
18
+ ## How do you do Server Rendering with React on Rails?
19
+ 1. The `react_component` view helper method provides the `prerender:` option to switch on or off server rendering.
20
+ 1. Configure your Webpack setup to create a different server bundle per your needs. While you may reuse the same bundle as for client rendering, this is not common in larger apps for many reasons, such as code splitting, handling CSS and images, different code paths for React Router on the server vs. client, etc.
21
+ 1. You need to configure `config.server_bundle_js_file = "server-bundle.js"` in your `config/initializers/react_on_rails.rb`
22
+ 1. You should ***not*** put a hash on the server-bundle so that you can easily use the webpack-dev-server for client bundles and have the server bundle generated by a watch process.
23
+
24
+ ## Do you need server rendering?
25
+
26
+ Server rendering is used for either SEO or performance reasons.
27
+
28
+ ## Considerations for what code can run on in server rendering
29
+
30
+ 1. Never access `window`. Animations, globals on window, etc. just don't make sense when you're trying to run some JavaScript code to output a string of HTML.
31
+ 2. JavaScript calls to `setTimeout`, `setInterval`, and `clearInterval` similarly don't make sense when server rendering.
32
+ 3. Promises and file system access don't work when server rendering with ExecJS. Instead, you can use the Node renderer or [React on Rails Pro](https://www.shakacode.com/react-on-rails-pro/).