diff --git a/.rubocop.yml b/.rubocop.yml index 7dd7e917..0cb6ae06 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -86,4 +86,4 @@ Naming/RescuedExceptionsVariableName: Metrics/BlockLength: Exclude: - - 'test/**/*_test.rb' \ No newline at end of file + - 'test/**/*_test.rb' diff --git a/CHANGELOG.md b/CHANGELOG.md index b904bddb..ec347c85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,17 @@ If you need help upgrading `react-rails`, `webpacker` to `shakapacker`, or JS pa [#1209 2.7 Release Discussion](https://github.com/reactjs/react-rails/issues/1209) ## Unreleased -Changes since last non-beta release. +Changes since the last non-beta release. _Please add entries here for your pull requests that are not yet released._ +## [3.1.0] - 2023-08-15 + +#### Added +- Added option to replace `null`s in props with `undefined` via `config.react.null_to_undefined_props` in `config/application.rb` #1293 + +## [3.0.0] - 2023-08-14 + ### Breaking Changes - Requires separate compilations for server & client bundles if using Shakapacker (see [Webpack config](https://github.com/reactjs/react-rails/tree/master/test/dummy/config/webpack)) #1274 - Replaces WebpackManifestContainer, which searched for assets in the webpack manifest, with SeparateServerBundleContainer, which expects a single server bundle file & does not use the webpack manifest at all. #1274 @@ -555,6 +562,8 @@ _Please add entries here for your pull requests that are not yet released._ - Server rendering with `prerender: true` - Transform `.jsx` in the asset pipeline -[Unreleased]: https://github.com/reactjs/react-rails/compare/v2.7.1...master -[2.7.1]: https://github.com/shakacode/shakapacker/compare/v2.7.0...v2.7.1 -[2.7.0]: https://github.com/shakacode/shakapacker/compare/v2.6.2...v2.7.0 +[Unreleased]: https://github.com/reactjs/react-rails/compare/v3.1.0...master +[3.1.0]: https://github.com/reactjs/react-rails/compare/v3.0.0...v3.1.0 +[3.0.0]: https://github.com/reactjs/react-rails/compare/v2.7.1...v3.0.0 +[2.7.1]: https://github.com/reactjs/react-rails/compare/v2.7.0...v2.7.1 +[2.7.0]: https://github.com/reactjs/react-rails/compare/v2.6.2...v2.7.0 diff --git a/Gemfile.lock b/Gemfile.lock index 72d3db72..e32dd1b5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - react-rails (3.0.0) + react-rails (3.1.0) babel-transpiler (>= 0.7.0) connection_pool execjs diff --git a/README.md b/README.md index f1e2f804..293c2e42 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ Here's a testimonial of how ShakaCode can help, from [Florian Gößler](https:// Read the [full review here](https://clutch.co/profile/shakacode#reviews?sort_by=date_DESC#review-2118154). Here's [another review of a Shakapacker migration that led to more work](https://clutch.co/profile/shakacode#reviews?sort_by=date_DESC#review-2096078). -## Resouces +## Resources * [Click to join **React + Rails Slack**](https://reactrails.slack.com/join/shared_invite/enQtNjY3NTczMjczNzYxLTlmYjdiZmY3MTVlMzU2YWE0OWM0MzNiZDI0MzdkZGFiZTFkYTFkOGVjODBmOWEyYWQ3MzA2NGE1YWJjNmVlMGE). Then join the channel `#react-rails`. * If you are upgrading, you might consider migrating to the [react_on_rails](https://github.com/shakacode/react_on_rails) gem. * Source code example utilizing React-Rails: https://github.com/BookOfGreg/react-rails-example-app @@ -68,6 +68,8 @@ Read the [full review here](https://clutch.co/profile/shakacode#reviews?sort_by= - [Upgrading](#upgrading) - [2.7 to 3.0](#27-to-30) - [2.3 to 2.4](#23-to-24) +- [Other features](#other-features) + - [Replace `null` with `undefined` in props](#replace-null-with-undefined-in-props) - [Common Errors](#common-errors) - [Getting warning for `Can't resolve 'react-dom/client'` in React < 18](#getting-warning-for-cant-resolve-react-domclient-in-react--18) - [Undefined Set](#undefined-set) @@ -75,6 +77,7 @@ Read the [full review here](https://clutch.co/profile/shakacode#reviews?sort_by= - [HMR](#hmr) - [Related Projects](#related-projects) - [Contributing](#contributing) +- [Supporters](#supporters) @@ -528,7 +531,6 @@ use it like so: ReactUJS.getConstructor = ReactUJS.constructorFromRequireContext(require.context('components', true)); ``` - ## Server-Side Rendering You can render React components inside your Rails server with `prerender: true`: @@ -801,6 +803,26 @@ For the vast majority of cases this will get you most of the migration: - add `import PropTypes from 'prop-types'` (Webpacker only) - re-run `bundle exec rails webpacker:install:react` to update npm packages (Webpacker only) +## Other features + +### Replace `null` with `undefined` in props + +React-Rails converts `nil` to `null` while parsing props from Ruby to JavaScript. Optionally, you can configure React-Rails to parse `nil` values to `undefined` as per the following: + +```ruby +# config/application.rb +module TheAppName + class Application < Rails::Application + # ... + # Set to true to convert null values in props into undefined + config.react.null_to_undefined_props = true + # ... + end +end +``` + +More information in: [discussion#1272](https://github.com/reactjs/react-rails/discussions/1272). + ## Common Errors ### Getting warning for `Can't resolve 'react-dom/client'` in React < 18 @@ -857,7 +879,7 @@ By contributing to React-Rails, you agree to abide by the [code of conduct](http You can always help by submitting patches or triaging issues. Even offering reproduction steps to issues is incredibly helpful! -# Supporters +## Supporters The following companies support the development of this and other open-source projects maintained by ShakaCode by providing licenses to the ShakaCode team. ShakaCode stands by the usefulness of these products! diff --git a/gemfiles/base.gemfile.lock b/gemfiles/base.gemfile.lock index 6ef10e35..5f99a78e 100644 --- a/gemfiles/base.gemfile.lock +++ b/gemfiles/base.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - react-rails (2.7.1) + react-rails (3.0.0) babel-transpiler (>= 0.7.0) connection_pool execjs @@ -169,10 +169,6 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.9) - nokogiri (1.14.3-x86_64-darwin) - racc (~> 1.4) - nokogiri (1.13.8-x86_64-linux) - racc (~> 1.4) nokogiri (1.13.8-x86_64-linux) racc (~> 1.4) notiffany (0.1.3) @@ -238,10 +234,6 @@ GEM timeout (0.3.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - webdrivers (5.2.0) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (~> 4.0) websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) @@ -271,7 +263,6 @@ DEPENDENCIES react-rails! selenium-webdriver test-unit (~> 2.5) - webdrivers BUNDLED WITH 2.4.9 diff --git a/gemfiles/shakapacker.gemfile.lock b/gemfiles/shakapacker.gemfile.lock index 286151dd..868c8e3f 100644 --- a/gemfiles/shakapacker.gemfile.lock +++ b/gemfiles/shakapacker.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - react-rails (2.7.1) + react-rails (3.0.0) babel-transpiler (>= 0.7.0) connection_pool execjs @@ -244,10 +244,6 @@ GEM timeout (0.3.2) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - webdrivers (5.2.0) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (~> 4.0) websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) @@ -278,7 +274,6 @@ DEPENDENCIES selenium-webdriver shakapacker (= 7.0.2) test-unit (~> 2.5) - webdrivers BUNDLED WITH 2.4.9 diff --git a/gemfiles/sprockets_3.gemfile.lock b/gemfiles/sprockets_3.gemfile.lock index ced06bde..f4abc37e 100644 --- a/gemfiles/sprockets_3.gemfile.lock +++ b/gemfiles/sprockets_3.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - react-rails (2.7.1) + react-rails (3.0.0) babel-transpiler (>= 0.7.0) connection_pool execjs @@ -246,10 +246,6 @@ GEM turbolinks-source (5.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - webdrivers (4.2.0) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (>= 3.0, < 4.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -282,7 +278,6 @@ DEPENDENCIES sprockets-rails test-unit (~> 2.5) turbolinks (~> 5) - webdrivers BUNDLED WITH 2.4.9 diff --git a/gemfiles/sprockets_4.gemfile.lock b/gemfiles/sprockets_4.gemfile.lock index 2a4754fb..44d3fc66 100644 --- a/gemfiles/sprockets_4.gemfile.lock +++ b/gemfiles/sprockets_4.gemfile.lock @@ -1,7 +1,7 @@ PATH remote: .. specs: - react-rails (2.7.1) + react-rails (3.0.0) babel-transpiler (>= 0.7.0) connection_pool execjs @@ -246,10 +246,6 @@ GEM turbolinks-source (5.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - webdrivers (4.2.0) - nokogiri (~> 1.6) - rubyzip (>= 1.3.0) - selenium-webdriver (>= 3.0, < 4.0) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) @@ -282,7 +278,6 @@ DEPENDENCIES sprockets-rails test-unit (~> 2.5) turbolinks (~> 5) - webdrivers BUNDLED WITH 2.4.9 diff --git a/lib/react/rails/component_mount.rb b/lib/react/rails/component_mount.rb index 6aef2c72..701d22e7 100644 --- a/lib/react/rails/component_mount.rb +++ b/lib/react/rails/component_mount.rb @@ -56,7 +56,10 @@ def generate_html_options(name, options, props, prerender_options) unless prerender_options == :static html_options[:data].tap do |data| data[:react_class] = name - data[:react_props] = (props.is_a?(String) ? props : props.to_json) + data[:react_props] = props_to_json( + props, + null_to_undefined: Dummy::Application.config.react.null_to_undefined_props + ) data[:hydrate] = "t" if prerender_options num_components = @cache_ids.count { |c| c.start_with? name } @@ -67,6 +70,17 @@ def generate_html_options(name, options, props, prerender_options) html_options end + def props_to_json(props, options = { null_to_undefined: false }) + return props if props.is_a?(String) + return props.to_json unless options[:null_to_undefined] + + # This regex matches key:value with null values while ensuing no string with similar + # pattern gets matched. It doesn't include null values in arrays. + props.to_json + .gsub(/([^\\]":)null([,}\]])/, '\1undefined\2') # match simple null values + .gsub(/([^\\]":(\[[^\\"]+,|\[))null([,\]])/, '\1undefined\3') # Match nulls in array + end + def rendered_tag(html_options, &block) html_tag = html_options[:tag] || :div diff --git a/lib/react/rails/railtie.rb b/lib/react/rails/railtie.rb index 43a54989..1b2cec9c 100644 --- a/lib/react/rails/railtie.rb +++ b/lib/react/rails/railtie.rb @@ -12,6 +12,7 @@ class Railtie < ::Rails::Railtie config.react.jsx_transformer_class = nil # defaults to BabelTransformer config.react.camelize_props = false # pass in an underscored hash but get a camelized hash config.react.sprockets_strategy = nil # how to attach JSX to the asset pipeline (or `false` for none) + config.react.null_to_undefined_props = false # Set to true to convert null values in props into undefined # Server rendering: config.react.server_renderer_pool_size = 1 # increase if you're on JRuby diff --git a/lib/react/rails/version.rb b/lib/react/rails/version.rb index eb628bdb..337a2738 100644 --- a/lib/react/rails/version.rb +++ b/lib/react/rails/version.rb @@ -4,6 +4,6 @@ module React module Rails # If you change this, make sure to update VERSIONS.md # and republish the UJS by updating package.json and `bundle exec rake ujs:publish` - VERSION = "3.0.0" + VERSION = "3.1.0" end end diff --git a/package.json b/package.json index 24dc5e6f..0a7f2b11 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react_ujs", - "version": "3.0.0", + "version": "3.1.0", "description": "Rails UJS for the react-rails gem", "main": "react_ujs/index.js", "files": [ diff --git a/test/react/rails/component_mount_test.rb b/test/react/rails/component_mount_test.rb index b80bc3e5..69dbcd24 100644 --- a/test/react/rails/component_mount_test.rb +++ b/test/react/rails/component_mount_test.rb @@ -2,6 +2,7 @@ require "test_helper" +# rubocop:disable Metrics/ClassLength class ComponentMountTest < ActionDispatch::IntegrationTest module DummyRenderer def self.render(component_name, props, _prerender_options) @@ -128,5 +129,92 @@ def self.react_rails_prerenderer assert_equal %(