diff --git a/CHANGELOG.md b/CHANGELOG.md index d48979cb18..f304d6bbf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,17 @@ Changes since last non-beta release. *Please add entries here for your pull requests that are not yet released.* +- Ability to use with Turbo (@hotwired/turbo), as Turbolinks gets obsolete. + React on Rails Pro Node rendering [PR ...](https://github.com/shakacode/react_on_rails/pull/...) by [pgruener](https://github.com/pgruener). + + To configure turbo the following option can be set: + `ReactOnRails.setOptions({ turbo: true })` + ### [12.2.0] - 2021-03-25 #### Added - Ability to configure server react rendering to throw rather than just logging the error. Useful for React on Rails Pro Node rendering [PR 1365](https://github.com/shakacode/react_on_rails/pull/1365) by [justin808](https://github.com/justin808). - + ### [12.1.0] - 2021-03-23 #### Added - Added the ability to assign a module with a `call` method to `config.build_production_command`. See [the configuration docs](./docs/basics/configuration.md). [PR 1362: Accept custom module for config.build_production_command](https://github.com/shakacode/react_on_rails/pull/1362). @@ -67,7 +73,7 @@ invoked to return the React component. In that case, you won't need to pass any [PR 1268](https://github.com/shakacode/react_on_rails/pull/1268) by [justin808](https://github.com/justin808) See [docs/basics/upgrading-react-on-rails](./docs/basics/upgrading-react-on-rails.md#upgrading-to-v12) -for details. +for details. #### Other Updates * `react_on_rails` fully supports `rails/webpacker`. The example test app in `spec/dummy` was recently converted over to use rails/webpacker v4+. It's a good example of how to leverage rails/webpacker's webpack configuration for server-side rendering. diff --git a/node_package/src/ReactOnRails.ts b/node_package/src/ReactOnRails.ts index 47fc9a90bd..47ebe6950b 100644 --- a/node_package/src/ReactOnRails.ts +++ b/node_package/src/ReactOnRails.ts @@ -75,8 +75,9 @@ ctx.ReactOnRails = { * Set options for ReactOnRails, typically before you call ReactOnRails.register * Available Options: * `traceTurbolinks: true|false Gives you debugging messages on Turbolinks events + * `turbo: true|false Turbo (the follower of Turbolinks) events will be registered, if set to true. */ - setOptions(newOptions: {traceTurbolinks?: boolean}): void { + setOptions(newOptions: {traceTurbolinks?: boolean, turbo?: boolean }): void { if (typeof newOptions.traceTurbolinks !== 'undefined') { this.options.traceTurbolinks = newOptions.traceTurbolinks; @@ -84,6 +85,13 @@ ctx.ReactOnRails = { delete newOptions.traceTurbolinks; } + if (typeof newOptions.turbo !== 'undefined') { + this.options.turbo = newOptions.turbo; + + // eslint-disable-next-line no-param-reassign + delete newOptions.turbo; + } + if (Object.keys(newOptions).length > 0) { throw new Error( `Invalid options passed to ReactOnRails.options: ${JSON.stringify(newOptions)}`, diff --git a/node_package/src/clientStartup.ts b/node_package/src/clientStartup.ts index 6378d00ac3..2a54b3aa53 100644 --- a/node_package/src/clientStartup.ts +++ b/node_package/src/clientStartup.ts @@ -58,6 +58,14 @@ function turbolinksInstalled(): boolean { return (typeof Turbolinks !== 'undefined'); } +function turboInstalled() { + const context = findContext(); + if (context.ReactOnRails) { + return context.ReactOnRails.option('turbo') === true; + } + return false; +} + function reactOnRailsHtmlElements(): HTMLCollectionOf { return document.getElementsByClassName('js-react-on-rails-component'); } @@ -221,13 +229,20 @@ function renderInit(): void { // Install listeners when running on the client (browser). // We must do this check for turbolinks AFTER the document is loaded because we load the // Webpack bundles first. - if (!turbolinksInstalled() || !turbolinksSupported()) { + if ((!turbolinksInstalled() || !turbolinksSupported()) && !turboInstalled()) { debugTurbolinks('NOT USING TURBOLINKS: calling reactOnRailsPageLoaded'); reactOnRailsPageLoaded(); return; } - if (turbolinksVersion5()) { + if (turboInstalled()) { + debugTurbolinks( + 'USING TURBO: document added event listeners ' + + 'turbo:before-render and turbo:render.'); + document.addEventListener('turbo:before-render', reactOnRailsPageUnloaded); + document.addEventListener('turbo:render', reactOnRailsPageLoaded); + reactOnRailsPageLoaded(); + } else if (turbolinksVersion5()) { debugTurbolinks( 'USING TURBOLINKS 5: document added event listeners ' + 'turbolinks:before-render and turbolinks:render.'); diff --git a/spec/react_on_rails/react_component/render_options_spec.rb b/spec/react_on_rails/react_component/render_options_spec.rb index 197d413b42..648fff9bf0 100644 --- a/spec/react_on_rails/react_component/render_options_spec.rb +++ b/spec/react_on_rails/react_component/render_options_spec.rb @@ -75,8 +75,13 @@ def the_attrs(react_component_name: "App", options: {}) it "is memoized" do opts = described_class.new(the_attrs) + generated_value = opts.dom_id - expect(opts.dom_id).to eq opts.dom_id + expect(opts.instance_variable_get(:@dom_id)).to eq generated_value + expect(opts.instance_variable_get(:@dom_id)).to eq opts.dom_id + + opts.instance_variable_set(:@dom_id, "1234") + expect(opts.dom_id).to eq "1234" end end