React vs Angular2: The fight rages on
Google's Angular and Facebook's React are now most popular tools for building browser (and not only) applications. Both are great solutions. While Angular2 is still in beta it has been already tested by few Google developer teams - AdWords, GreenTea and Fiber. List of applications built with React is really long and you can find there names like Instagram, Netflix, PayPal and many more.
Brutal war is coming.
First blood
There is already a bloodstained Angular 2 versus React comparison out there (by Cory House). It touches some important, higher level characteristic of both fighters. First drops of blood were spit on the ground. But the real struggle is yet to begin…
Know your enemy
Choosing between Angular and React is like choosing between buying an off-the-shelf computer and building your own with off-the-shelf parts.
Cory House has also told us:
I can just add that React has great browser devtools extensions (Redux too). Sadly I could not find any for Angular2.
Arena
To compare these front end technologies I made a little TODO application. To make things even simpler I prepared single Redux core for both applications (inspired by Angular 2 — Introduction to Redux). Both are written in TypeScript so comparison is even clearer. You can find the source code used for this comparison on GitHub:
Redux Core: https://github.com/evojam/redux-todo-lib
Angular2 App: https://github.com/evojam/angular2-redux-sample-app
The Fight
The core of both technologies is a component. A unit of view. Both expect your app to be a tree of components. Also both encourage us to pass data to a component tree by its root vertex. Root-to-leaves data flow is what should make application "more functional", so let's go!
Round I: Functional Components
In such arborescence based application each vertex is a component. Each component:
accepts some data from parent node (I'll call it
input
)returns a sub tree of components (
view
)
In both Angular2 and React input
is handled by passing data from an element to its children by sub nodes properties (not html attributes). In both solutions view
is more or less an XML tree.
TodoList component
A reusable, filterable, simple list of todos needs two pieces of information - array of todos (ITodo[]
) and way of filtering (FilterType
enum value). So our components input can look like this:
And components used anywhere in the app:
React:
Angular2:
And this is React component definition:
And Angular2 version:
Without any doubt React version is stateless, pure and simple. It takes data and returns DOM. Wow!
It works but it does not compile properly. I do not know if it's TypeScript .tsx
support issue or React typings problem (please post a comment if you know the origin of this issue). Anyway, it failed so I had to switch to a class
based React component. It holds state - 'props' is now member of component - but 'props' can still be treated as immutable input in functional approach:
Angular version has much more configuration. It seems to be too much for such simple example. It is there because Angular does not mix JS with HTML (directives
, host
, pipes
, selector
, templateUrl
) and because of more sophisticated change detection mechanics (changeDetection
).
The selector
is a way to bind component JS definition with element in template - in React it's not needed as JS component is used as template element in JSX.
The pipes
and directives
are here to inform component what other components, directives and pipes (inline html filters) would be used in template - again JS components are used directly in JSX templates. React does not provide anything like templateless directives (for me it is a problem and I'll mention it later) nor inline pipes (because we can use JS pure functions).
The templateUrl
is self-describing enough I think, isn't it? And it is considered a downside. But I really appreciate possibility to separate bigger templates from component file. Lack of compile time template check is of course Angular's weakness.
The host
appears because of main difference in template rendering.
React's component function (or render method) returns the whole component tree with one vertex:
In Angular component is bound to an element (by selector
mentioned above) which becomes root node of components tree. This root element is called host. So in Angular template we only put contents of the host element:
And if we want to add anything (like a css class, attribute value) to host element we can declare it in the host
definition - e.g. {'class':'todo-list'}
will add "todo-list"
class to <ul [todoList]=… ></ul>
. We can also bind dynamic values and listeners to host element using TypeScript decorators on component's properties.
Todo component
So we'll try now to add some event handlers. My Angular Todo component looks like this:
We get ITodo
entity as an input value. Output is a host element with two buttons. I have also bound static "todo"
and conditional "done"
css classes to the host element. Button clicks trigger corresponding methods of component's todoActions
member. Not much magic. Only this (click)=…
syntax may hurt purists' eyes. We can always drop sugar and use plain <button on-click=… >
instead (or bind-anything=…
instead of [anything]=…
).
And React version:
We get ITodo
entity as an input value. Output is an element with two buttons. The root element of returned XML tree has static "todo"
and conditional "done"
css classes. Until now it's quite similar. But I see here two quite irritating features of React:
in
onClick
handlersthis
context is lost - we have to do tricks (bind or curry)class attribute has to be fed with its whole value - no CSS class management mechanics
I also don't like className
and htmlFor
properties. They have to be used because class
and for
are reserved words in JS. Looks like we have to pay somehow for mixing HTML with JS…
Combine: React
Todo
TodoList
Somewhere higher in arborescence:
And the output:
Combine: Angular2
Todo
TodoList
Somewhere higher in arborescence:
And the output:
Round I Verdict
React is a king of little, simple, one purpose components. If your application can be easily expressed as a tree of simple data-to-view transformations React seems to be the best answer. And it's undoubtedly the most functional view rendering system we can use. The more event handling and/or sophisticated UI the less Angular2 boilerplate is painful. As a matter of fact, Angular2 component configuration and binding annotations inconvenience tend to be inversely proportional to components complication. And furthermore it still has functional approach (not so pure, but sticks to idea data => view
. Probably some day Reactangular or Angulareact framework will bring us possibility to efficiently utilize both approaches in one application. But we still have to choose.
Functional or not, it should make developer's work painless in any possible way. Readability of code is one of them and it seems HTML is crucial in this comparison.
Round II: View aesthetics
Let's prepare a new component with a more complex structure - the ListOfLists.
Angular2
React
Round II Verdict
Is there anything I should say? Choose what you like more!
Ok. In my opinion:
is much more readable than:
In some places mixing JS code with XML values is a mess.
A matter of taste.
Round III: Change detection
Let's state here one important thing - DOM update (after change was detected) is handled similarly in both frameworks. They update only the parts that actually should be changed. We'll see now what is the difference in detecting if a change have occurred.
React
React basic mechanics are simple. If state
or props
of component where changed - change handlers are called:
state
triggers change whensetState()
method is calledprops
change occurs when parent component re-renders
There is also possibility to trigger change by calling forceUpdate()
. Change detectors are triggered only in subtree where change occurred. Simple and optimal. The only little downside is that we would have to setState()
manually in the root vertex of our arborescence.
You probably have noticed a key={todo.id}
prop in examples. It's the mechanism used for detecting changes of list elements. If a value passed to key
property has changed - the HTML representing a related element is re-rendered. Quite verbose, but sane. Downside - when using a strict typing in your projects, you'll be forced to define a key: string;
additional property on all your "listed" components.
Angular
Angular team decided to choose a slightly different approach. They have incorporated zone.js to plug into asynchronous browser callbacks (setTimeout, setInterval, event handlers and XMLHttpRequest events). And when any of these is called Angular runs change detection. You can find great in-depth explanation here. What is more interesting - you can decide to choose a change detection strategy on any component of your application tree.
In the example application I used an OnPush
strategy so all my components check for updates only when any of their @Input()
properties have changed. So a change detection in my Angular application acts almost exactly as in React one. There are actually few more strategies: CheckOnce
, Checked
, CheckAlways
, Detached
, OnPush
and Default
. For some explanation see ChangeDetectionStrategy docs.
Just like React's key={…}
Angular has its own way to handle list elements changes - NgFor.ngForTrackBy
. In a code it looks like this:
The _byId
passed here is a function of form list => list.id
. So quite similar to React - we have to create some unique identifier for each element in the list so a change detector knows if it is still the same element. Maybe slightly more gentle than in React, but you have to learn more framework specific syntax.
Round III Verdict
While both solutions offer a quite sane out of the box approach React's default change detection is without a doubt better optimized. On the other hand Angular's under the hood detection is definitely a game changer in web development as it plugs into the native browser asynchronous layer so responsibility for broadcasting possible changes is taken away from the developer. Of course both ideas also have their downsides. React component's changes have to be triggered manually if data does not come from props, but straight from the store (basic Flux architecture). If you would like to make Angular application follow more functional approach, you will have to define proper detection strategy explicitly in each single component.
Nevertheless both are great. Angular gives more flexibility with cleaner configuration tools, so this time React looses.
Round IV: Extending HTML
Doing what?? Just adding some functionality to an HTML element. And making it easily reusable on many elements.
Angular
A simple example above shows how we can take some value from an input property, bind some other to element property or add an event listener to an element. And it will be applied to any HTML element with inp-alerter
attribute. And this is only a small part of what can be done. Actually @Directive
decorated classes have almost all superpowers that can be used in @Components
, but do not have templates. Simple and powerful.
React
There is a possibility to use React mixins to achieve similar functionality. But it would be a great dose of overengineering and could be error prone. Anyway, a developer has to create a component for each set of element functionalities used in the application.
K.O.
Final round brought pain and gore. React's guts are flowing out and brain is smeared on the stage…
Thanks God it's only a metaphor.
In a world almost conquered by Angular 1.x and ready for Web Components a mechanism to extend HTML elements behavior is a MUST HAVE. Angular2 won this round by K.O. but I'm sure that React will do the homework.
The Curtain Falls
It's obvious that React and Angular2 share many similarities. They represent similar approaches to application architecture and handling data. On the other hand every functionality is implemented in a completely different way (ok, lifecycle hooks for components are almost identical). These differences do not however imply difference in difficulty of application development. Both solutions provide a sufficient set of robust tools to build a well organized, scalable and reactive application core.
Of course React is smaller and covers only view/component responsibility - and that is what I have compared here. Lack of engine to add simple functionalities to HTML elements is actually the only Reacts undisputed fault.
Angular2 is more mature, flexible and complete solution. But it's new - still in beta - and has advantage over the enemy because it has incorporated ideas from both Angular1 and React experience.