Guides
Rails
Javascript
Additional details
Deployment
React on rails pro
Api
Misc
Contributor info
Testimonials
Outdated
Shakacode logoShakaCodeDeveloped by

Copyright 2020 ShakaCode

File-System-Based Automated Bundle Generation

To use the automated bundle generation feature introduced in React on Rails v13.1.0, please upgrade to use Shakapacker v6.5.1 at least. If you are currently using webpacker, please follow the migration steps available here.

Configuration

Enable nested_entries for Shakapacker

To use the automated bundle generation feature, set nested_entries: true in the webpacker.yml file like this. The generated files will go in a nested directory.

default:
  ...
  nested_entries: true

For more details, see Configuration and Code section in shakapacker.

Configure Components Subdirectory

components_subdirectory is the name of the matched directories containing components that will be automatically registered for use by the view helpers. For example, configure config/initializers/react_on_rails to set the name for components_subdirectory

config.components_subdirectory = "ror_components"

Now all React components inside the directories called ror_components will automatically be registered for usage with react_component and react_component_hash helper methods provided by React on Rails.

Configure auto_load_bundle Option

For automated component registry, react_component and react_component_hash view helper method tries to load generated bundle for component from the generated directory automatically per auto_load_bundle option. auto_load_bundle option in config/initializers/react_on_rails configures the default value that will be passed to component helpers. The default is false, and the parameter can be passed explicitly for each call.

You can change the value in config/initializers/react_on_rails by updating it as follows:

config.auto_load_bundle = true

Update .gitignore file

React on Rails automatically generates pack files for components to be registered in the packs/generated directory. To avoid committing generated files into the version control system, please update .gitignore to have

# Generated React on Rails packs
app/javascript/packs/generated

Note: the directory might be different depending on the source_entry_path in config/webpacker.yml.

Usage

Basic usage

Background

If the webpacker.yml file is configured as instructed here, with the following configurations

default: &default
  source_path: app/javascript
  source_entry_path: packs
  public_root_path: public
  public_output_path: packs
  nested_entries: true
# And more

the directory structure will look like this

app/javascript:
  └── packs:               # sets up webpack entries
  │   └── application.js   # references FooComponentOne.jsx, BarComponentOne.jsx and BarComponentTwo.jsx in `../src`
  └── src:                 # any directory name is fine. Referenced files need to be under source_path
  │   └── Foo
  │   │   └── ...
  │   │   └── FooComponentOne.jsx
  │   └── Bar
  │   │   └── ...
  │   │   └── BarComponentOne.jsx
  │   │   └── BarComponentTwo.jsx
  └── stylesheets:
  │   └── my_styles.css
  └── images:
      └── logo.svg

Previously, many applications would use one pack (webpack entrypoint) for many components. In this example, theapplication.js file manually registers server components, FooComponentOne, BarComponentOne and BarComponentTwo.

import ReactOnRails from 'react-on-rails';
import FooComponentOne from '../src/Foo/FooComponentOne';
import BarComponentOne from '../src/Foo/BarComponentOne';
import BarComponentTwo from '../src/Foo/BarComponentTwo';

ReactOnRails.register({ FooComponentOne, BarComponentOne, BarComponentTwo });

Your layout would contain:

  <%= javascript_pack_tag 'application' %>
  <%= stylesheet_pack_tag 'application' %>

Suppose, you want to use bundle splitting to minimize unnecessary javascript loaded on each page, you would put each of your components in the packs directory.

app/javascript:
  └── packs:               # sets up webpack entries
  │   └── FooComponentOne.jsx # Internally uses ReactOnRails.register
  │   └── BarComponentOne.jsx # Internally uses ReactOnRails.register
  │   └── BarComponentTwo.jsx # Internally uses ReactOnRails.register
  └── src:                 # any directory name is fine. Referenced files need to be under source_path
  │   └── Foo
  │   │   └── ...
  │   └── Bar
  │   │   └── ...
  └── stylesheets:
  │   └── my_styles.css
  └── images:
      └── logo.svg

The tricky part is to figure out which bundles to load on any Rails view. Shakapacker's append_stylesheet_pack_tag and append_javascript_pack_tag view helpers enables Rails views to specify needed bundles for use by layout's call to javascript_pack_tag and stylesheet_pack_tag.

Solution

File-system-based automated pack generation simplifies this process with a new option for the view helpers. The steps to use it in this example are:

  1. Remove parameters passed directly to javascript_pack_tag and stylesheet_pack_tag.
  2. Remove parameters passed directly to append_javascript_pack_tag and append_stylesheet_pack_tag.

Your layout would now contain:

  <%= javascript_pack_tag %>
  <%= stylesheet_pack_tag %>
  1. Create a directory structure as mentioned below:
app/javascript:
  └── packs
  └── src:
  │   └── Foo
  │   │ └── ...
  │   │ └── ror_components          # configured as `components_subdirectory`
  │   │   └── FooComponentOne.jsx
  │   └── Bar
  │   │ └── ...
  │   │ └── ror_components          # configured as `components_subdirectory`
  │   │   │ └── BarComponentOne.jsx
  │   │   │ └── BarComponentTwo.jsx
  1. You no longer need to register these React components nor directly add their bundles. For example you can have a Rails view using three components:
    <%= react_component("FooComponentOne", {}, auto_load_bundle: true) %>
    <%= react_component("BarComponentOne", {}, auto_load_bundle: true) %>
    <%= react_component("BarComponentTwo", {}, auto_load_bundle: true) %>

If FooComponentOne uses multiple HTML strings for server rendering, the react_component_hash view helper can be used on the Rails view, as illustrated below.

<% foo_component_one_data = react_component_hash("FooComponentOne",
                                             prerender: true,
                                             auto_load_bundle: true
                                             props: {}
                                          ) %>
<% content_for :title do %>
   <%= foo_component_one_data['title'] %>
<% end %>
<%= foo_component_one_data["componentHtml"] %>

The default value of the auto_load_bundle parameter can be specified by setting config.auto_load_bundle in config/initializers/react_on_rails.rb and thus removed from each call to react_component.

Server Rendering and Client Rendering Components

If server rendering is enabled, the component will be registered for usage both in server and client rendering. In order to have separate definitions for client and server rendering, name the component files as ComponentName.server.jsx and ComponentName.client.jsx. The ComponentName.server.jsx file will be used for server rendering and the ComponentName.client.jsx file for client rendering. If you don't want the component rendered on the server, you should only have the ComponentName.client.jsx file.

Note: If specifying separate definitions for client and server rendering, please make sure to delete the generalized ComponentName.jsx file.

Using Automated Bundle Generation Feature with already defined packs

To use the Automated Bundle Generation feature with already defined packs, config/initializers/react_on_rails should explicitly be configured with config.auto_load_bundle = false and you can explicitly pass auto_load_bundle option in react_component and react_component_hash for the components using this feature.