In this lesson, we're going to migrate our React Calendar Appointments app from react-rails to using the react_on_rails gem. We'll see what the differences are and it'll help you understand the concepts we outlined in the previous lesson in more detail.
Note that in this lesson, we're building on top of the app we built in lesson 4.4, when we were still using the react-rails gem (not 4.5 where we used the webpacker gem).
We're first going to remove the react-rails gem from the Gemfile and replace it with react_on_rails.
Note about react_on_rails gem version
At the time of recording this lesson, the latest react_on_rails gem version was 6.4.2, which did not depend on the yarn package manager, and only used npm.
However, the latest version of the gem requires yarn. So you can either specify the exact version in your Gemfile like this:
gem 'react_on_rails', '6.4.2'
Or if you want to use the latest gem with yarn, you can install yarn first and then replace all npm install commands in the lesson with yarn.
Next, let's remove the React and other utility JavaScript libraries (moment.js and react-datetime) from the application.js file. We're going to install moment.js and react-datetime from npm, so we can remove them from here. And we don't need components.js either.
Another thing we need to remove is the addons config setting in application.rb Remove the React addons setting in application.rb Then let's install the react_on_rails gem and run through the setup process quickly.
Then we need to change the entry point for webpack in the client/app/bundles/Appointments/startup/registration.jsx file:
import ReactOnRails from 'react-on-rails';
import Appointments from '../components/appointments';
// This is how react_on_rails can see the Appointments in the browser.
ReactOnRails.register({
Appointments,
});
Replace all instances of HelloWorld with Appointments.
Let's also delete the 3rd party library javascript files (moment and react-datetime) from the vendor assets directory.
Let's leave utils.js in app/assets for now because we'll use it later.
This is because of one big change compared to react-rails. Because we had all our React component code in the app/assets directory, all components were automatically getting included in the asset pipeline.
But now that we have our components in the client directory (outside app/assets), we have to explicitly specify when we want to use code across files. That's the standard practice in using JavaScript modules using import and export statements.
The only thing that gets included in the asset pipeline by default is the final bundle that Webpack produces.
So let's fix this error by importing React in appointments.jsx:
import React from 'react';
We also need to export the Appointments component:
export default class Appointments extends React.Component {
Now if we refresh the page, the React and Appointments component errors are gone. But we get a different error - AppointmentForm is not defined.
We need to change the component file extensions: Change component file extensions from .es6.jsx to .jsx Let's remove .es6 because we haven't set webpack to recognise that extension. We're using es6 by default anyway so we don't need it. We can just use the .jsx extension.
So let's first export that in appointments_list.jsx:
import React from 'react';
export const AppointmentsList = ({appointments}) =>
Because it's a const, we just say export const, which makes it a named export. When we import it in appointments.jsx, we need to enclose it in curly braces, because it's a named import:
import { AppointmentsList } from './appointments_list';
If you want to avoid using named exports and imports, you can make it a default export by defining it below the component definition:
export default AppointmentsList
Ok now let's refresh and see what more fun awaits us! Datetime is not defined! I know this can be a bit annoying that we are getting so many errors and warnings. But I'm purposely doing this step by step so you can see how react_on_rails and ES6 works.
Ok, let's fix the Datetime error by installing react-datetime from npm.
We just need to add it to our package.json file (the one in the client directory). Put it in the list of dependencies:
"react-datetime": "2.8.3",
And then run:
$ npm install
We get an unmet peer dependency error because react-datetime needs moment.js and we haven't installed it. We need moment anyway, so let's also add that to package.json and run npm install again.
Once that's done we can run foreman again.
$ foreman start -f Procfile.dev
Remember we're not including anything by default. So we still need to import Datetime in our Appointments component:
import Datetime from 'react-datetime'
Now if we refresh, the Datetime error is fixed. But we get a different error: Appointments: Cannot read property 'map' of undefined That's coming from the AppointmentsList component, where we use map to loop through the appointment records:
Now let's try and make an appointment. Set a title and choose a date.
When I click on a date, the title gets reset. Looks like our state updates are not quite working correctly. If I enter the title again and submit the form, the appointment doesn't get added to the appointments list.
Let's have a look at the console. We have an error: obj is not defined That's coming from the handleChange function in AppointmentForm:
Now if we refresh, we can set a title and choose a date without any errors. The title doesn't get reset. If we submit the form, we get an error: Cannot read propery 'update' of undefined
That's coming from the addNewAppointment function in appointments.jsx because we don't have React addons. So let's add that.
Now although we used the update function from React addons in our previous lessons, this package has now been deprecated. The React team recommends using another package called immutability-helper. So let's use that.
This package has an update function which works just like the one we've been using. So let's add it to the list of dependencies in our package.json file:
"immutability-helper": "2.1.1",
And run:
$ npm install
And we need to restart our processes with foreman.
Then let's import update into the appointments.jsx file:
import update from 'immutability-helper';
And we can replace React.addons.update with just update in the addNewAppointment function:
Now if we refresh, we can enter a title, choose a date and submit the form successfully. The appointment gets created and added to the appointments list.
It all works perfectly now!
So we've migrated our app from using the react-rails gem, to using the react_on_rails gem with Webpack.
We had to make some big changes to make this work.
We moved all of our React components code from the default Rails app/assets directory to a new client directory.
We also started using npm (or yarn) to install JavaScript dependencies.
And finally, we had to explicitly import and export all the components and utility libraries.
This lesson was part of the The Complete React on Rails 5 Course.