The performance decoy
In the summer of 2014, we were already using AngularJS internally for one of our products, the ActiveMonitor front-end which, incidentally, has just recently been merged with the new version of ActiveUI. It was therefore very tempting to select AngularJS as our preferred technology so that we could draw upon the knowledge acquired.
In order to decide between Angular and React, a decision was made to prototype a pivot table that would display a large amount of data in real-time, with no further optimization. During that comparison, the React prototype appeared to be much more responsive. However, it was later found that this difference in performance had been due to a missing “track by” keyword in the AngularJS prototype. Fixing it resulted in comparable performance rates. Amusingly, this recent article explains that exactly the same error has been made over and over again on some of the online benchmarks comparing these frameworks. At least, we learned that ReactJS performance would come mostly for free when writing an Angular application would mean being very careful in order to avoid a number of pitfalls such as the one mentioned above.
A different audience
In terms of popularity, it is hard to beat Angular. Backed by the giant Google and being one of the first frameworks to pose a credible alternative to the still popular jQuery, it is undeniable that Angular has a lot of momentum. But Angular targets a broader and less technical audience than React.
Google Trends shows us that Angular is definitely more popular than React:
‘Interest over Time’ in the two frameworks as of September 2016 – Credit: https://www.google.com/trends/
But in terms of technology, the difference is not so marked. Looking closely at the popularity of the Github repositories, Angular and React are respectively the 5th and 6th most starred repositories of Github as of September 2016, each with a very similar number of stars (52.000 for Angular and 49.000 for React). And the trends on GitHub stars indicate that React will soon overtake Angular.
Also, if we try to build the “stupidity index” of each of the repositories, by comparing the number of forks with the number of stars, we end up with 15.5% for React and 45.4% for Angular.
Finally, by looking at where the Google queries are coming from, we can make the extrapolation that Angular is used mostly in India, whereas React is more distributed across all the developed countries. This is a sign that the React technology is still new but trendy, while Angular is more mature and predominantly used in outsourced projects.
ReactJS search around the globe on September 2016 – Credit: https://www.google.com/trends/
Eat your own dog food
Although Angular was popular back in 2014, there were far fewer examples of applications made with Angular. React, however, is developed and used in production by Facebook. This makes a real difference, because it suggests immediately that it is possible to build a huge and scalable application to deal with large real-time datasets coming from the server, which is exactly the use case of ActiveUI.
A more flexible approach
It is also worth noting that React deals purely and exclusively with the rendering of your application. That means you have to make extra choices regarding the way you interact with the server, inject your dependencies, implement internationalization, handle logging or organize your code for example.
On the other hand, Angular is more opinionated and attempts to govern all aspects of a client-side application. While this might be handy for some use cases, using React means you are free to choose the best framework or to implement your own concept for a given feature. It even becomes possible later on to challenge that choice without questioning React.
Another difference is that React views are defined programmatically using JSX whereas Angular requires the creation of a controller and a template. This means that, with React, it is much easier to use inheritance, composition or aggregation to enrich your view, whereas with Angular you would need to split your view into multiple templates. And being able to easily change part of the view implementation is one of our requirements.
An uncertain future
Finally, following the Angular developers meeting notes and articles , it was clear that Angular version 2 would deny a lot of strong principles introduced with Angular 1, such as scope and controllers. This would mean making one of the following two choices:
- Pick version 1 and expect a true rewrite or
- Pick version 2, which was clearly not stable enough at that time.
For all the reasons described above, we chose React over Angular. Would it have been possible to achieve the new version of ActiveUI with Angular? Probably. But after two years there are no regrets about the decision that we made. If we were given the opportunity to do it all over again, we would still make the same decision.
Taking advantage of React
React comes with a set of innovative concepts. Even though very attractive, they were not per se the reasons why we chose React. But by understanding them, we were able to leverage those concepts to quickly provide a powerful and useful set of features.
Using the same code, with React, we can see that the Virtual DOM can batch DOM updates and perform a ‘diff’ with the previous state of the DOM to ask the browser to only re-render the minimal necessary changes.
Additionally, React components encourage functional programming and are written in a declarative way. Why does it matter? Because it decreases the complexity of your code!
Consider a simple example: suppose there is a requirement to build a bell icon that displays the number of alerts that have been received. Three icons are available to be displayed: a bell, a snowflake and a fire. If there are less than 5 alerts, a snowflake icon must be added to the bell, if there are more than 100 alerts a fire icon must be added. Otherwise you simply display the bell icon.
The state of a component defines what it will look like and how it will behave. In the example above, we have as many different states as alert counts. But we can summarize those states into three different groups:
|1||Alert count <= 5||snowflake|
|2||5 < Alert count < 100||regular|
|3||Alert count >= 100||fire|
Then if we were to write our code using imperative programming, such as jQuery, we would have to handle 6 different cases (i.e. 6 conditions):
|1||A → B||remove snowflake|
|2||A → C||remove snowflake and add fire|
|3||B → A||add snowflake|
|4||B → C||add fire|
|5||C → A||remove fire and add ice|
|6||C → B||remove fire|
Using React, we would only have to handle 3 cases (i.e. 3 conditions):
More generally speaking, writing your code in an imperative way means you have to handle all the transitions between the states. By writing it in a declarative way, you only need to handle the states. Thus if your application has n states, the comparative complexity can be seen in the following table:
|Coding Method||Number of States||Complexity|
Naturally, a price must be paid for decreasing the complexity of your code. React must transform the Virtual DOM back into the minimal necessary transition for the real DOM. But the claim of React is that, overall, it will be more efficient than asking the browser to re-render everything, or to perform non-optimal separate transitions.
Immutability and Batched Rendering
However, in order to push the performance further and extend the features of our application, we wanted to integrate the same concepts that David Nolen introduced in the Om library.
Every React component implements a ‘shouldComponentUpdate’ method that controls when to render or not. This is usually decided by comparing the current state and props (properties) of a component with its previous state and props. To ensure that this is done efficiently, it is necessary to avoid deep object comparison and only manipulate immutable data.
The fact that every single component goes through a ‘global state’ prior to being rendered means the whole application can be serialized into JSON at any stage. Although it forces the developers to organize their code in a very specific way, it provides the following abilities without significant effort:
- Undo/redo: the ability to undo, or re-apply a number of previous actions
- Restore/reload: the ability to save the application state and reload it at any moment
- Synchronization: the ability to synchronize two components by sharing their state through a server
- Debug: the ability for a client, while investigating an error, to dump the application state for us to reload it
Overall, the change that was made to the technology stack was a massive leap forwards. We were able to build a completely new user interface with a solid modern stack. The multitude of frameworks we now use has also minimized the likelihood of having to completely rewrite the application in one block. This means we can evolve with the environment more easily. Changing frameworks and language can be a tough decision, but it is vital to remember that the way you design and architect your application is also huge part of that success!