Article summary
For the past two years, I’ve been chugging along in the Angular world. I’ve become very familiar with Angular concepts, best practices, and how to resolve the infamous circular dependencies error. But as things go at Atomic, I had the opportunity to switch projects and be thrust into an entirely different tech stack. Here, I talk about my journey learning React, the main differences between Angular and React, and some helpful tips to learn React when you’ve primarily been working with Angular.
Angular vs. React
One of the main differences between Angular and React is that Angular is a full model-view framework, while React is a JavaScript library that just focuses on the view layer. Frameworks come packaged with everything you need to create a full-fledged application. When using React, you must import other libraries to accomplish data fetching, routing, and state management.
You can think of a framework as being a full-service restaurant and libraries as grocery stores. When going to a restaurant, you are given a menu (well-defined structure), and the staff (built-in tools and services) guide and serve you throughout the meal. The restaurant has a specific way of doing things, from seating you at a table, taking your order, cooking your food according to their recipes, and serving it to you.
Now, consider going to a grocery store. Here, you’re free to choose any ingredients you like and cook your meal at home. You can experiment with different recipes (customization), and you have complete control over every step of the process. However, you’re also responsible for knowing how to use these ingredients and cook the meal.
Components
React only has components. There are no directives, pipes, or modules like there are in Angular. As a result, this means less boilerplate. React components can be class-based or functional. Function components with the use of hooks are the preferred approach.
import React from 'react';
const HelloWorld = () => {
return (
<div>
<h1>Hello, World!</h1>
</div>
);
}
export default HelloWorld;
In contrast, Angular components are strictly class-based, using the @Component decorator to define the component. Angular also uses modules to register components.
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-hello-world',
templateUrl: './hello-world.component.html',
styleUrls: ['./hello-world.component.css']
})
export class HelloWorldComponent {
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { HelloWorldComponent } from './hello-world/hello-world.component';
@NgModule({
declarations: [
AppComponent,
HelloWorldComponent
],
imports: [
BrowserModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
The DOM
The DOM is a hierarchical representation of the structure of a web document, most commonly HTML. Angular uses change detection to track changes and update the DOM. React, instead, uses a virtual DOM. The virtual DOM is an in-memory representation of actual DOM elements. When changes are made, React compares the virtual DOM with the actual DOM and only updates the parts that have changed. This process is known as reconciliation. Angular uses zone.js to detect changes. When changes occur, Angular runs a change detection algorithm to update the view. While this happens automatically, reducing the cognitive load of the developer, it is also less efficient.
Data Binding
When working in Angular for so long, getting used to how data is passed between a parent component and a child component can be a little intimidating. React uses unidirectional data binding, meaning that data is only passed down from a parent to a child component. Angular has a way to easily bind data in both directions using the @Input() and @Output() decorators.
parent.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
parentData = 'Hello from Parent';
handleDataChange(newData: string) {
this.parentData = newData;
}
}
parent.component.html
<app-child [childData]="parentData" (childDataChange)="handleDataChange($event)"></app-child>
child.component.ts
import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.css']
})
export class ChildComponent {
@Input() childData: string;
@Output() childDataChange = new EventEmitter<string>();
handleChange(event: Event) {
const newData = (event.target as HTMLInputElement).value;
this.childDataChange.emit(newData);
}
}
child.component.html
<input [value]="childData" (input)="handleChange($event)" />
<p>{{ childData }}</p>
While two-way data binding in React is possible, I would argue that it’s not as straightforward as in Angular. To have the child component update the parent component’s state, you must pass a function from the parent to the child to handle the updates.
Parent Component
import React, { useState } from 'react';
import ChildComponent from './ChildComponent';
const App = () => {
const [data, setData] = useState('Hello from Parent');
const handleDataChange = (newData) => {
setData(newData);
};
return (
<div>
<ChildComponent data={data} onDataChange={handleDataChange} />
</div>
);
}
export default App;
Child Component
import React from 'react';
const ChildComponent = ({ data, onDataChange }) => {
const handleChange = (event) => {
onDataChange(event.target.value);
};
return (
<div>
<input type="text" value={data} onChange={handleChange} />
<p>{data}</p>
</div>
);
}
export default ChildComponent;
*ngFor and *ngIf
Another key difference is how React and Angular iterate over an array to render multiple elements. Angular uses the *ngFor structural directive within the component’s template.
<ul>
<li *ngFor="let user of users">{{ user.name }}</li>
</ul>
React uses the Javascript map() method within the component’s return statement. To track each element in React, you should include a unique key prop. Achieve this in Angular by using a trackBy function.
fucntion UserList ({ users }) {
return (
<ul>
{users.map(user => (
<li key=(user.id)>{user.name}</li>
))}
</ul>
);
}
This can be achieved in Angular by using a trackBy function.
app.component.html
<ul>
<li *ngFor="let user of users; trackBy: trackById">{{ user.name }}</li>
</ul>
app.component.ts
trackById(index: number, user: any): number {
return user.id;
}
I think Angular simplifies the process by embedding the logic directly in the HTML. Similarly, Angular uses the *ngIf directive within the component’s template. Whereas, you would achieve the same functionality in React by using an if statement within the return statement of the component.
While there may be many differences between Angular and React, both have their places depending on the project. Angular is an opinionated, robust framework but may require too much boilerplate, while React is lightweight and extremely customizable. Angular has a “default” way of doing things, whereas there are many ways to accomplish the same thing in React. Choose wisely and happy coding!