Migrating Angular 1.x apps to React — The Hybrid Way!

In this day and age when front-end space is moving incredibly fast, it’s pretty common where by the time you do your R&D, choose your framework, build your app and release to production, it’s already obsolete! At the same time, it’s imperative to build PoCs with the new, bright and shiny out there and see if it makes sense or performs better in your use case.

This read is useful for those who are looking for a strategy to migrate large production Angular apps to React but can’t move forward because of the pain and time consuming process of re-writing directives into React components. In this read, I will talk about a hybrid approach which can be used to iteratively build new features with React components and use them in your existing Angular app or your future React app. The idea is to construct independent React components and render them by usingReactDOM.render function locally instead of globally. I adopted this approach to incrementally migrate the front-end of our web apps ecosystem from Angular 1.x to React. I’ll also discuss the motivation behind it, Chrome JS profiler experiment to calculate performance gain and why I chose react.

To give you a little background about myself, I’m a lead full stack engineer at @WalmartLabs e-commerce division. We are a small team of 3–5 full stack engineers, building and supporting an ecosystem of web apps that are primarily built on Node/Express/Angular 1.x stack. We use Webpack 2 as our task runner with code written in ES6 that gets transpiled into ES5 through Babel. The client side is an angular SPA which talks to the Node layer. The Node layer integrates with Couchbase DB and other backend systems through REST APIs with JSON being the data exchange format. We have about 25 front-end reusable angular custom directives shared across 3 web apps.

Now, let’s talk about the approach which essentially uses the existing ng controllers and embeds react views in those.

The game of Hybrid

The prerequisite for this approach is that you are using angular router with decoupled controllers and dependencies.

It’s independent of ES6 and can be written with ES5 syntax.

The goal is to independently render react or angular views on different route invocations. If it’s a react view, it should be able to utilize angular $scope variables or properties defined in $rootScopeand should be able to call event handlers defined in $scope

Given below is my sample angular controller which renders a React view

Here, the controller function myController instantiates certain constructor variables and functions, invokes getResultsfunction defined in myAngularServiceforAjaxCalls. In the success callback, $scope is populated with the service response.

As we move along to line 37, my customrender function invokes ReactDOM.render function which means we are now in react context and can use custom react components. Quick thing to call out- you need to encapsulate ReactDOM.renderwith a custom render function so that you only render your react view once the promise is resolved.

Another thing to notice here is I’m attaching changePageto $scope.This is because I want to stay in angular $scope context. This can directly be attached to your function context akathisas well.

My pure react components, WMPageHeader, WMBreadcrumb, and WMGrid are added as the controller dependencies so they can be invoked inside the view and props can be populated with controller scoped vars and objects.

Given below is an example of my react component. As you can see here, there is no angular dependency whatsoever which means these components are 100% react and re-usable with any other react app. This is another advantage of this approach.

Now, let’s take a look at the change we need to make in the main app view. Here in my index.hbsI simply added an id attr render-containerfor my react views. data-ui-view stays the same for angular views.

This is it. We are all done! Now, I can take up the re-write work iteratively and only convert those angular directives that are needed in my new feature/view. Once, I achieve 100% conversion, I can simply discard my old angular directives and keep building my app with react!

Motivation

It’s not a hidden fact that Angular 1.x is pretty notorious in terms of performance when it comes to nested scopes and watchers. The problem becomes more prominent when you have custom directives built on top of open source directives. Imagine these directives composing a view that also involves multiple Ajax requests.

That being said, angular performance would have been pretty normal to me if I had not discovered the gains with React.

Performance gain

As an experiment, I decided to take the most complex view of the app and re-created it with React.

Here’s what Chrome JS Profiler has to say:

Chrome JS Profiler results with Angular
Chrome JS Profiler results with React

As you can see above, there is a 35.5% reduction in scripting, 62.5% in rendering and 39% in Painting with an overall reduction of 1.5 seconds in view rendering!

Why did I choose React?

There were obvious technical reasons to try out React over anything else. It makes rendering/re-rendering of DOM nodes lightning fast. Not to mention it’s revolutionary concept of virtual DOM and only updating real DOM when needed through tree diff mechanism.

Also, Electrode being @WalmartLabs homegrown platform for web apps which is built on top of React and Hapi, it’s always better to have Electrode ready React components for any future web app to be written with it.

twitter: @ankuronweb