How We Migrated from AngularJS to React/Redux in 2 Weeks

In January 2017 when my business partner and I decided to build the MVP of our FinTech SaaS platform Systelos, I chose to build it in AngularJS. This decision was made with the understanding that the codebase would later be migrated to a newer Javascript framework.

Sure ReactVue and Angular 2+ were superior frameworks, but they were not valid options for us at the time due to the following reasons:

  • Prior to Systelos I spent two years as CTO for a VC-backed blockchain company where I did very little hands-on coding. I was excited to roll up my sleeves and write code again but I knew it would not be easy. For these reasons, I wanted a technology I was most comfortable with (AngularJS) especially with our deadline of three months.
  • AngularJS still had the largest community of developers, so I knew that finding talent would not be difficult.
  • I like to be practical and efficient, so my plan was to hack through the development process using open-source packages available and the AngularJS community was more likely to have what I was looking for.

Fast forward two years later, I find myself once again less hands-on in my role with a great team that actively develops and has prior experience with React.

Moreover, AngularJS was starting to show its limitations.

The problem

The bigger our codebase got, the harder it became to maintain. One of our biggest problems was that every ng- attribute{{expression}} and $scope.$watch contributed to slowing down the $digest cycle of our application. There was only so much that we could do to reduce the number of $$watchers on our pages. To combat this we tried to remove as much two-way binding as possible, but we still ended up with thousands of watchers and a slow digest cycle, making our app fairly unresponsive and sluggish.

This, combined with some code reusability issues and the fact that AngularJS was no longer being actively developed, made me look into possibly rewriting the whole application.

Why we chose React

The decision to use React over other modern frameworks like Vue and Angular was an easy one.

  • React’s Virtual DOM would solve the performance issues we previously had. It achieves this by keeping track of changes and updating only the necessary parts of the DOM that changed without rebuilding the entire DOM.
  • Its Component structure would allow us to re-use and share components across our codebase. These components reflect a particular part of the user-interface, which would be shared across multiple pages.
  • Styled-components solve so many CSS problems I’ve experienced personally. How many times have you had to fix a CSS bug that was a caused as a result of classes overwriting each other?
  • React Native would help us write business logic once and reuse it for both mobile and web.

How we migrated

The obvious solution to migrate to React would be a complete rewrite. However, we’re a fast moving startup and couldn’t afford to stop delivering features to our customers while we rewrite everything. Instead we decided on a progressive migration strategy, where new features would be written in React and existing features would be gradually replaced over time.

In the paragraphs below, I’ll describe the steps we took to migrate our application from AngularJS to React.

Step 1: From Gulp to Webpack

First we had to remove Gulp to start using Webpack, which integrates nicely with React. Run the following command to install Webpack.

npm install webpack webpack-cli webpack-dev-server — save-dev

Webpack requires a file (or files) to be defined as entry point. These files should require/import all the app’s dependencies and files needed to run the application. To do this, we created five files:

vendors.js — This file imports all of the dependencies (scripts only) from node_modules that the app needs.

https://gist.github.com/arsenetoumani/c4b25aa0c5bba869491fdd8dac057070

vendors-styles.js — This file imports all of the dependencies (styles only) from node_modules that the app needs.

https://gist.github.com/arsenetoumani/96ff66787f9de23d7560cc983956454a#file-vendor-styles-js

app-styles.js — This file imports all the LESS and CSS files from our app.

https://gist.github.com/arsenetoumani/1bfe72ccb9145d34d77b4f345463e3a5#file-app-styles-js

app-templates.js — This file grabs all the HTML files from our app and saves them in template cache so they’ll be available when the app is running.

https://gist.github.com/arsenetoumani/bf90607a2655f687024b02d3b75379ea#file-app-templates-js

app.js — This file imports all the app’s JS and JSX files and combines them with the other app files from above (HTML, LESS, CSS).

https://gist.github.com/arsenetoumani/be73ae625b1495ae97a68e2190afc983#file-app-js

The final webpack.config file looks something like this: https://gist.github.com/arsenetoumani/0e4ff6d718d7bb254f48acd443184316

Check out these articles for more instructions on setting up Webpack;

Step 2: Ditch Bower for NPM

It’s no longer recommended to use Bower since it’s being deprecated and they advise developers to move to Yarn (in our case we used NPM). So we moved the dependencies that were defined in bower.json to package.json.

However, there were some issues we had to overcome.

Issue #1: Some Bower packages have different versions and names compared to their NPM equivalents.

Solution: Find those packages manually on the NPM official site.

Issue #2: Some packages depend on other packages of the same version. For instance packages “angular-animate”, “angular-aria”, “angular-cookies”, and “angular-messages” depend on “angular”; so if those packages versions don’t match, the application will throw errors when trying to register the modules.

Solution: Figure out which packages depend on which and make sure their versions are compatible. As for the packages that depend on AngularJS, their versions need to match the Angular version the app is using.

Step 3: Setup Babel

In order for Webpack to transpile ES6 syntax — which we intend to use, babel-loader was needed.

First we installed the following packages:

npm install @babel/core babel-loader @babel/preset-env @babel/preset-react — save-dev

Next we created a .babelrc file in the root directory (where package.json is) and added the following presets:

  • @babel/preset-env for ES6, ES7, and ES8 files
  • @babel/preset-react for React Files
{
 “presets”: [
 “@babel/preset-env”,
 “@babel/preset-react”
 ]
}

In the webpack.config.js file, we then needed to configure Webpack to load/transpire JS and JSX files with Babel. To do this, we added the following rule in the webpack.config.js

{
 module: {
 rules: [
 {
    test: /\.(js|jsx)$/, //rule for js and jsx files
    use: ‘babel-loader’,
    exclude: /node_modules/
 },
 ….
}

Step 4: Unit Test with Jest and Karma

Setting up Karma

Due to our existing tests written in AngularJS, we needed to find a way to also run the new React tests alongside. To do this, we configured KarmaJest andEnzyme.

Webpack tends to fail and throw errors when reading our spec files due to the AngularJS dependencies included. For example, a spec file with the following code will throw errors stating that the injected dependencies are undefined.

beforeEach(inject(function (_$controller_, _$rootScope_, $httpBackend, _$uibModal_, FiltersService) {…}

So we configured it to not process .spec.js files.

Setting up Jest & Enzyme

Our Jest config file looks something like this:

https://gist.github.com/arsenetoumani/373af9c3cd8177590a43fdbb7036a2c5#file-jest-config-js

Our Enzyme config file looks something like this:

https://gist.github.com/arsenetoumani/451b55cba65c7a72dd2d3a762de1d4bf#file-enzyme-config-js

Results: our app is 3x faster

With every engineering project we undertake, metrics are very important to us. They help us gauge how much better we are as a team, and how many of our goals are met.

So we ran a test between two pages of our application; one written in React and the other in AngularJS. These are the results:

You can see from these results our app is much faster when written in React, mainly thanks to the Virtual DOM. It does a great job managing DOM updates compared to AngularJS which redraws the entire DOM every time small changes happen.

Conclusion

There is no best way to migrate from AngularJS to React/Redux — it all depends on your team, product requirements and timing. I chose a practical and efficient strategy at Systelos where we focused first on implementing the basic configuration and tooling needed to run React alongside our AngularJS app.

In my next blog post I’ll discuss how we write React components alongside AngularJS and how we’ll be progressively migrating the rest of our application. Stay tuned for part 2!

Leave a Reply

Your email address will not be published. Required fields are marked *