Our journey from Rails to React, pt 2: migrating views incrementally
As explained in part 1, before we even started migrating from Rails to React, we already had our components organized, thanks to Atomic Design. It was time for the real migration to start!
Swapping the whole project at once could take us several weeks. We like to work in small iterations that continuously add value to our users, so we needed to find a way to switch our views incrementaly.
Hacking the /public folder
Our plan was to switch views to React, one URL at a time, while Rails would keep serving the rest as before.
We installed Gatsby (our React framework of choice) in a /static_generator
folder in the root of our project. Then, we configured our npm run build
task to move the static files, generated by Gatsby, from /static_generator/public
to /public
.
Example:
/static_generator/pages/my-url.js
Gets compiled as:
/static_generator/public/my-url/index.html
Moved to
/public/my-url/index.html
Thus, accessible as
mydomain.com/my-url/
One small caveat of this approach was that, to prevent git conflicts, we needed to delete everything inside /public
and add that folder to .gitignore
. To keep old Rails static files working, we first moved them to /static_generator/static
. Gatsby copies everything in that folder to /static_generator/public
so, at the end of the build process, those files were moved back to their original /public
folder. What a roundtrip!
With this, we got files in the /public
folder served by Rails and URLs worked fine. We didn’t even need to care about controllers or routes for that. However, for dynamic URLs (such as mydomain.com/my-resource/(UUID)/
) there was not a direct match between Gatsby files and final URLs.
In those cases, we had to create a page with dynamic params in Gatsby, first:
/static_generator/pages/resources/[uuid].js
Strings in brackets [] are available as params in Gatsby.
Then, we had to explicitly tell our Rails controllers to return the static files:
class ResourceController < WebController
def show
render file: ‘public/resources/[uuid]/index.html’, layout: false
end
end
With this, we were able to render the views, but those still had no access to Rails data. We added a simple API for that, which React consulted with the uuid
param.
Automatic deploys (or kind of)
The last step was launching the builds automatically. Those take up some time, so we hoped to be able to run gatsby build
just when changes in the /static_generator
folder were merged into master. Changes to other parts of the project shouldn’t trigger unnecessary builds. Sadly, we couldn’t.
Heroku reads the root package.json
of a project automatically on each build, caching node_modules
. However, reading package.json
inside other folders doesn’t work (or we couldn’t find a way 😅, if you know one please leave a comment).
We had to duplicate the dependencies in both files and call the child from the root file:
“scripts”: {
“build”: “cd static_generator && ../node_modules/.bin/gatsby clean && ../node_modules/.bin/gatsby build — prefix-paths && cp -R public ../”,
}
Summary
The process has a couple of downsides, but those don’t outweight the most important benefit: we didn’t have to monopolize all of our development resources in the migration for weeks and could, instead, migrate gradually.