Skip to content

Latest commit

 

History

History
275 lines (214 loc) · 7.75 KB

react.md

File metadata and controls

275 lines (214 loc) · 7.75 KB

React Integration

These steps describe creating a Rails/React app, using Shakapacker as the bundler.

Before starting, ensure that you have Yarn installed, for example:

npm i -g yarn

Easy Setup

If you'd like easy integration of React with Ruby on Rails, see React on Rails.

The below information applies to a React on Rails app, except that you need to setup some environment variables as follows:

EXECJS_RUNTIME=Node

Basic Manual Setup

Create a new Rails app as per the installation instructions in the README.

Add React, as well as the necessary libraries to enable CSS support in your application:

yarn add react react-dom @babel/preset-react
yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin

Update the Babel configuration in the package.json file:

"babel": {
  "presets": [
    "./node_modules/shakapacker/package/babel/preset.js",
+   "@babel/preset-react"
  ]
},

And that's it. You can now create a React app using app/javascript/application.js as your entry point.

Enabling Hot Module Replacement (HMR)

With HMR enabled, Shakapacker will automatically update only that part of the page that changed when it detects changes in your project files. This has the nice advantage of preserving your app’s state.

To enable HMR in a React app, proceed as follows:

In config/shakapacker.yml set hmr is set to true.

Install the react-refresh package, as well as @pmmmwh/react-refresh-webpack-plugin:

yarn add --dev react-refresh @pmmmwh/react-refresh-webpack-plugin

Alter config/webpack/webpack.config.js like so:

const { generateWebpackConfig, inliningCss } = require('shakapacker');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const isDevelopment = process.env.NODE_ENV !== 'production';

const webpackConfig = generateWebpackConfig();

if (isDevelopment && inliningCss) {
  webpackConfig.plugins.push(
    new ReactRefreshWebpackPlugin({
      overlay: {
        sockPort: webpackConfig.devServer.port,
      },
    })
  );
}

module.exports = webpackConfig;

This applies the plugin to the webpack configuration.

Delete the Babel configuration from package.json:

- "babel": {
-   "presets": [
-     "./node_modules/shakapacker/package/babel/preset.js",
-     "@babel/preset-react"
-   ]
- },

Then create a babel.config.js file in the root of project and add the following:

module.exports = function (api) {
  const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
  const resultConfig = defaultConfigFunc(api)
  const isDevelopmentEnv = api.env('development')
  const isProductionEnv = api.env('production')
  const isTestEnv = api.env('test')

  const changesOnDefault = {
    presets: [
      [
        '@babel/preset-react',
        {
          development: isDevelopmentEnv || isTestEnv,
          useBuiltIns: true
        }
      ]
    ].filter(Boolean),
    plugins: [
      isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
        {
          removeImport: true
        }
      ],
      process.env.WEBPACK_SERVE && 'react-refresh/babel'
    ].filter(Boolean),
  }

  resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
  resultConfig.plugins = [...resultConfig.plugins, ...changesOnDefault.plugins ]

  return resultConfig
}

This is taken from the sample React Babel config.

HMR for your React app is now enabled. 🚀

A Basic Demo App

To test that all of the above is working, you can follow these instructions to create a basic React app using Shakapacker.

  1. Create a new Rails app:
rails new myapp --skip-javascript
cd myapp
bundle add shakapacker --strict
./bin/bundle install
./bin/rails shakapacker:install
yarn add react react-dom @babel/preset-react
yarn add css-loader style-loader mini-css-extract-plugin css-minimizer-webpack-plugin
  1. Generate a controller
rails g controller site index
echo '<div id="root"></div>' > app/views/site/index.html.erb
  1. Create a CSS file and a React component:
touch app/javascript/App.css app/javascript/App.js
  1. Edit app/javascript/application.js like so:
import React from 'react';
import { createRoot } from 'react-dom/client';
import HelloMessage from './App';

const container = document.getElementById('root');
const root = createRoot(container);

document.addEventListener('DOMContentLoaded', () => {
  root.render(<HelloMessage name="World" />);
});
  1. Add the following to app/javascript/App.js:
import React from 'react';
import 'App.css';
const HelloMessage = ({ name }) => <h1>Hello, {name}!</h1>;
export default HelloMessage;
  1. Add the following to app/javascript/App.css:
h1 { color: blue; }
  1. Enable HMR in config/shakapacker.yml:
hmr: true
  1. Install the react-refresh package, as well as @pmmmwh/react-refresh-webpack-plugin:
yarn add --dev react-refresh @pmmmwh/react-refresh-webpack-plugin
  1. Alter config/webpack/webpack.config.js like so:
const { generateWebpackConfig, inliningCss } = require('shakapacker');
const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin');
const isDevelopment = process.env.NODE_ENV !== 'production';

const webpackConfig = generateWebpackConfig();

if (isDevelopment && inliningCss) {
  webpackConfig.plugins.push(
    new ReactRefreshWebpackPlugin({
      overlay: {
        sockPort: webpackConfig.devServer.port,
      },
    })
  );
}

module.exports = webpackConfig;
  1. Remove the Babel configuration from package.json
- "babel": {
-   "presets": [
-     "./node_modules/shakapacker/package/babel/preset.js"
-   ]
- },
  1. Create a babel.config.js file in the project root and add the following sample code:
module.exports = function (api) {
  const defaultConfigFunc = require('shakapacker/package/babel/preset.js')
  const resultConfig = defaultConfigFunc(api)
  const isDevelopmentEnv = api.env('development')
  const isProductionEnv = api.env('production')
  const isTestEnv = api.env('test')

  const changesOnDefault = {
    presets: [
      [
        '@babel/preset-react',
        {
          development: isDevelopmentEnv || isTestEnv,
          useBuiltIns: true
        }
      ]
    ].filter(Boolean),
    plugins: [
      isProductionEnv && ['babel-plugin-transform-react-remove-prop-types',
        {
          removeImport: true
        }
      ],
      process.env.WEBPACK_SERVE && 'react-refresh/babel'
    ].filter(Boolean),
  }

  resultConfig.presets = [...resultConfig.presets, ...changesOnDefault.presets]
  resultConfig.plugins = [...resultConfig.plugins, ...changesOnDefault.plugins ]

  return resultConfig
}
  1. Start the Rails server and the shakapacker-dev-server in separate console windows:
rails s
./bin/shakapacker-dev-server
  1. Hit: http://localhost:3000/site/index

  2. Edit either the React component at app/javascript/App.js or the CSS file at app/javascript/App.css and observe the HMR goodness.

Note that HMR will not work if you edit app/javascript/application.js and experience a full refresh with a warning in the console. For more info on this, see here: pmmmwh/react-refresh-webpack-plugin#177