Yet another “React vs Angular” comparison. However, this time, let’s compare the required effort. After, using Angular and React for considerable period of time, I wondered about the total effort one may have to put in order to perform certain trivial tasks using these tools. At end of the day, as a developer, we would always strive to use the fastest tool to accomplish our tasks.
I will keep the illustrations to a very basic level and will try to avoid setup steps as much as possible. This way, we will be able to clearly focus on the main points and highlight the differences.
Let’s get started.
Simple component to render a view
With React
import React from 'react';const App = ({title}) => (
<div>{title}</div>
)<HelloWorld title="Hello world" />
With Angular
//app.component.ts
import {Component, Input} from "@angular/core"@Component({
selector: "app",
template: `<div>{{title}}</div>`
})
export class AppComponent {
@Input() title: string = ""
}
/*
This component needs to be declared in some module and, that module needs to be imported in the rendering components module. Otherwise this component will not render.
*///app.module.ts
import {NgModule} from "@angular/core";@NgModule({
declarations: [HelloWorldComponent]
exports: [HelloWorldComponent]
})
export class AppModule {}<app title="Hello world"></app>
Conditional Rendering
With React
import React from 'react';const WithConditions = ({booleanProp})=> {
return (
booleanProp ?
<div>When its true</div> :
<div>When its false</div>
);
}<WithConditions booleanProp />
With Angular
//with-condition.component.ts
import {Component, Input} from "@angular/core"@Component({
selector: "with-condition",
template: `
<div *ngIf="booleanProp; else elseTemplate">When its true</div>
<ng-template #elseTemplate>
<div>When its false</div>
</ng-template>
`
})
export class WithConditionComponent {
@Input() booleanProp: boolean = true;
}
...
Some more code for declaration and export of component in a module
...<with-condition [booleanProp]="true"></with-condition>
Iteration over elements of an Array.
With React
import React from 'react';const WithArray = ({array}) => array.map((item)=> (
<div key={item}>{item}</div>
));
<WithArray array={[1,2,3,4]} />
With Angular
//with-array-items.component.ts
import {Component, Input} from "@angular/core"@Component({
selector: "with-array",
template: `
<div *ngFor="let item of array">{{item}}</div>
`
})
export class WithArrayComponent {
@Input() array: number[] = [];
}<with-array [array]="[1,2,3,4]"></with-array>
Content projection
With React
import React from 'react';const WithChildContent = ({children}) => (
<section>
<h1>This is a added heading</h1>
{children}
</section>
);<WithChildContent>
<p>Child content</p>
</WithChildContent>
With Angular
import {Component, Input} from "@angular/core"@Component({
selector: "with-child-content",
template: `
<section>
<h1>This is a added heading</h1>
<ng-content></ng-content>
</section>
`
})
export class WithChildContentComponent {}<with-child-content>
<p>Child content</p>
</with-child-content>
Dynamic component rendering
With React
import React from 'react';const One = () => <span>One</span>;
const Two = () => <span>Two</span>;const WithDynamic = ({componentKey, componentsMap})=> {
const DynamicComponent = componentsMap.get(componentKey);
return (<DynamicComponent />);
}
const componentsMap = new Map()
.set("one", One)
.set("two", Two);<WithDynamic
componentKey="one"
componentsMap={componentsMap}/>
With Angular
import {Component, Input, ComponentFactoryResolver, ViewContainerRef, OnInit, ViewChild} from "@angular/core"@Component({
selector: "one",
template: `<span>One</span>`
})
export class OneComponent {}@Component({
selector: "two",
template: `<span>Two</span>`
})
export class TwoComponent {}@Component({
selector: "with-dynamic-component",
template: `
<ng-container #dynamicComponentContainer>
</ng-container>
`
})
export class WithDynamicComponent implements OnInit {
@Input() componentKey: string;
@Input() componentsMap: Map = new Map();
@ViewChild("dynamicComponentContainer", {
read: ViewContainerRef,
static: true
})
private dynamicComponentContainer: ViewContainerRef; constructor(private componentResolver: ComponentFactoryResolver) {}
ngOnInit(): void {
this.dynamicComponentContainer.clear();
const cmpRef = this.componentsMap.get(this.componentKey);
const cmpFactory = this.componentResolver
.resolveComponentFactory(cmpRef);
const cmpInstance = this.dynamicComponentContainer
.createComponent(cmpFactory).instance;
}
}const componentsMap = new Map()
.set("one", OneComponent)
.set("two", TwoComponent);<with-dynamic-component
componentKey="one"
[componentsMap]="componentsMap">
</with-dynamic-component>
Two way data binding
With React
import React, {useState} from 'react';const WithTwoWayDataBinding = () => {
const [value, setValue] = useState("");
return (
<section>
<h1>{value}</h1>
<input
type="text"
value={value}
onChange={e => setValue(e.target.value)} />
</section>
)
};
<WithTwoWayDataBinding />
With Angular
import {Component, Input} from "@angular/core"@Component({
selector: "with-two-way-data-binding",
template: `
<section>
<h1>{{value}}</h1>
<input
type="text"
[(ngModel)]="value"
/>
</section>
`
})
export class WithTwoWayDataBindingComponent {
value = "";
}<with-two-way-data-binding></with-two-way-data-binding>
Component interaction and data sharing.
With React
import React from 'react';const Child = ({notifyParent})=> {
return <button onClick={()=> notifyParent("Hello")}>Notify</button>
}
const Parent = ()=> {
const [childValue, setChildValue] = useState("");
const onNotified = value => setChildValue(value); return (<Child notifyParent={onNotified}/>)
}
<Parent />
With Angular
import {Component, Input, Output, EventEmitter} from "@angular/core"@Component({
selector: "child",
template: `
<button (click)="notifyParent.emit('Hello')">Notify</button>
`
})
export class ChildComponent {
@Output() notifyParent: EventEmitter<string> = new EventEmitter();
}@Component({
selector: "parent",
template: `
<child (notifyParent)="onNotified($event)"></child>
`
})
export class ParentComponent {
childValue = "" onNotified(value: string) {
this.childValue = value;
}
}<parent></parent>
Avoiding unnecessary rendering
With React
React does not re-render the components until and unless we are changing the component’s state or props. Moreover, React uses virtual DOM and diff algorithm to find the difference between previous and latest DOM after the state change is triggered. If the changes are observed, it is scheduled to be reflected in the browser’s DOM.
With this, the rendering/re-rendering is completely in the control of the developer and no extra effort is required for avoiding the unnecessary rendering.
With Angular
Angular runs change detection cycles to evaluate the template expressions.
During this, it compares previous and new values of the template expressions, if any difference is found, it updates the view.
This change detection cycle runs on every asynchronous operation such as browser events, XHRs, timers etc. takes place. With this default behavior, it is very easy to create a performance mess. For example, binding a heavy lifting function to the template could slow down the entire app.
To avoid such things to happen, one could switch to ChangeDetectionStategy.OnPush and take control of rendering. With this strategy, the component and its child components are skipped from default change detection tree. Now the change detection will only be triggered
when component’s input reference changes, its child component emits event, template linked pipes emits new value or change detection is triggered manually. Let’s see an example for this.
import {Component, Input, ChangeDetectionStrategy, OnInit, ChangeDetectorRef} from "@angular/core"@Component({
selector: "on-push",
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<span>{{timerValue}}</span>
`
})
export class OnPushComponent implements OnInit {
timerValue = 0;
constructor(private cdr: ChangeDetectorRef) {} ngOnInit():void {
interval(()=> {
this.timerValue =+ 1000;
this.cdr.markForCheck();
}, 1000)
}
}<on-push></on-push>
Getting data from server and displaying results.
With React
import React, {useState, useEffect} from 'react';const App = () => {
const [posts, setPosts] = useState([]);
const [error, setError] = useState(null); useEffect(()=> {
fetch("https://api.example.com/post")
.then(res => res.json())
.then(posts => setPosts(posts))
.catch(e => setError(e))
});
return (
<section>
{ error
? <span>Something went wrong</span>
: posts.map(post => <span key={post.id}>{post.title}<span>)
}
</section>
)
}<App/>
With Angular
import {Component, Input, OnInit, OnDestroy} from "@angular/core"
import { HttpClient } from "@angular/common/http";
import { of, Subject} from "rxjs";
import { catchError, takeUntil} from "rxjs/operators";@Component({
selector: "app",
template: `
<ng-container *ngIf="!error; else errorTemplate">
<div *ngFor="let post of posts">{{post.title}}</div>
</ng-container>
<ng-template #errorTemplate>
<span>Something went wrong</span>
</ng-template>
`
})
export class App implements OnInit, OnDestroy {
posts:{title: string}[] = [];
error;
private destroy$ = new Subject(); constructor(private http: HttpClient) { }//A better approach here is to use async pipes.
ngOnInit() {
this.http.get("https://api.example.com/post")
.pipe(
catchError((e)=> {
this.error = e;
return of([]);
}),
takeUntil(this.destroy$)
)
.subscribe(posts => (this.posts = posts));
} // Its necessary to unsubscribe in order to avoid any memory leaks.
ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}<app></app>
Conclusion
There are many more patterns and tasks that we could look into, and the list will be endless. However, by comparing steps of these trivial tasks between Angular and React, it is quite evident that Angular is verbose, quite complex, has steep leaning curve and requires more effort to implement a given flow. This is quite understood from the fact that Angular is more inclined towards OOP paradigm and probably it borrows many of its patterns from Java/.NET world.
On the other hand, React is more inclined towards FP (Functional Programming) and probably it borrows many patterns from NodeJS world. With release of React hooks, it’s more evident that React is trying to stay away from strict OOP paradigm. With this, React looks quite straight forward and anyone with a good knowledge of JavaScript could quickly use it. Given the fact that most Front End developers are well versed with JavaScript, learning curve of React will be low for them.
Nevertheless, both are best in what they do. They follow certain established programming principles and paradigm. It’s just about our personal preference and of course, when in an enterprise setup, it’s the team’s preference towards the paradigm they can more relate to.