Converting Angular apps to React has become popular as developers seek smaller and speedier projects. They are two of the most popular JavaScript frontend frameworks out there, but as projects grow in size, functionality, and complexity, it can become necessary to migrate from one framework to another.
Migrating will give your engineering team the opportunity to update your projects to the standards expected of modern apps and better react to the ever-changing facets (primarily in terms of maintenance) of the software development lifecycle, and migrating from Angular to React is no exception to this.
In this article, you will learn about the considerations you should make when converting Angular apps to React, migration strategies, the advantages and disadvantages of migrating, why you may want to migrate; and then finally we will do a walkthrough of the process of converting an Angular app to React, which you can follow along with at your own pace. Let’s get started.
Jump ahead:
- Things to note when migrating from Angular to React
- What to do before migrating
- Some reasons teams may migrate from Angular to React
- Migration Strategies
- Why you may not want to migrate from Angular to React
- Converting an Angular app to react
- The project we will convert
- Defining the Angular Services
- Creating the header component
- Creating the button component
- Creating the Add Task component
- Creating the task item and task components
- Insights from the conversion process
Things to note when migrating from Angular to React
Angular and React are great frontend frameworks with distinct characteristics that make them fundamentally different, and you must take note of the following when migrating:
- React uses a virtual DOM, while Angular works with a real DOM
- Angular has two-way binding, while React has one-way binding
- Angular comes with TypeScript out of the box; React does not
- Angular has built-in support for AJAX, HTTP, and Observables, and React does not.
- React has a large ecosystem and direct support from Meta (Facebook), while Angular is only supported by the developer community
- Angular comes with out-of-the-box features like validation, component-scoped CSS, animations, conditional rendering, and more
- The Angular CLI enables easy generation of components, modules, and other features that help developer productivity
What to do before migrating Angular to React
Set goals to avoid scope creep
Many development teams suffer the consequences of not laying down the scope when working on projects.
What is the goal and focus of this migration? Will it strictly be a one-and-done migration, or will you use the opportunity to make updates, bug fixes, and performance optimizations along the way? Note that the answers to these questions will affect the delivery timeline of the migration.
Carry out an audit to understand pain points and blockers
What limitations and inefficiencies of your current system require a switch? It is important that the new stack you are switching to removes existing blockers without adding new ones. Ensure that there is a valid reason to migrate before you do so and you’re not carrying over issues.
Choose the right migration strategy
What migration strategy will you use? Will you stagger the migration release? Choosing the right strategy is key to a successful migration and maintaining expectations.
Some reasons teams may migrate from Angular to React
The needs of businesses and development teams change over time, and sometimes those changes require a transition of tech stacks to adapt.
Here are the key reasons you may migrate from Angular to React:
- The adoption of React is continually increasing among developers and new crops of web development talents focus on learning the skills with the greatest market demand. This means that it will become increasingly difficult to find or hire Angular developers to manage projects and you may need to switch to React as a matter of necessity
- React has a broader range of flexibility than Angular — you can easily find blog templates, animation libraries, multiple component libraries, toast libraries, and more. Compared to Angular, React has a larger and more active open source community that continually develops third-party libraries, templates, online courses, and other resources
- Business needs and focuses change over time, and developers may be required to build mobile apps to serve more customers. It will be easier and faster for them to adopt a React codebase to work with React Native
Migration strategies
Migrating an app’s codebase is complex, and while there is no easy way to do this, there are several strategies we can explore.
Rewrite
A complete rewrite from the ground up is considered a better way of migrating applications in terms of quality because you get to start with a new application, define an architecture that fits the new framework, and there is no need to deal with integrating old and new code.
However, this strategy has drawbacks — it requires more time and resources, and the delivery timeline can be challenging to estimate.
Strangler pattern
React and Angular are component-based frameworks and you can take advantage of this when defining your migration strategy. Component-based migration means you can break your Angular app into individual parts and migrate them one after the other.
Since the development of different components can progress in parallel, you can have separate teams migrating different parts of the app — with this strategy, you can stagger releases and deploy the migration for each component.
A disadvantage is that you will have to integrate the new framework’s architecture with that of the old framework.
Why you may not want to migrate from Angular to React
An enduring benefit of Angular is that it makes decision making easier because of its opinionated system and built-in tools — developers don’t need to spend days and weeks meeting to discuss the libraries for state management or data fetching.
Angular is built with TypeScript, and working with TypeScript out-the-box reinforces clean code and makes debugging and maintenance easier down the line — scaling issues aside.
Moving from Angular to React will mean losing out on the Angular CLI, which developers can use to create repeatable blocks of code like components, services, pipes, and directives from the command line.
Converting an Angular app to React
Having looked at the background involved in framework migration, let’s convert an Angular app to React and see how it’s done.
We will use Next.js for the React app; however, the same principles and code snippets can be adopted into a vanilla React app.
To get started, follow the steps in these Angular and Next.js guides to spin up new Angular and React applications.
The project we will convert
We will convert a simple task tracker app from Angular to React; the image below shows the app.
When the Add button is clicked, it reveals a modal where we can add new tasks.
These are the different components the app is made up of:
- Header: The app’s header
- Button: A reusable button component
- Add Task: A form component responsible for adding new tasks
- Task Item: The individual task item
- Tasks: The component where we render all the tasks
Let’s start creating the components. We will look at the Angular code first, and then convert that to React.
(Note: Working knowledge of Angular and Next.js is required to follow along with the rest of this article)
Defining the Angular services
Services are blocks of functionality an app needs to complete a specific task or to carry out an operation. Services are a critical part of adding functionality to Angular apps; learn more about them here.
There are two services the Angular application will need, a Task service and a UI service.
Creating the Task service
This will be in charge of fetching tasks from a mock API, adding new tasks, and deleting tasks.
The code below does the following:
getTasks
fetches the tasksdeleteTask
adds new tasksaddTask
deletes tasks
//language: JavaScript import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { Task } from 'src/app/mock-tasks'; import { HttpClient, HttpHeaders } from '@angular/common/http'; const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', }), }; @Injectable({ providedIn: 'root', }) export class TaskService { private apiUrl = 'http://localhost:5000/tasks'; constructor(private http: HttpClient) {} getTasks(): Observable<Task[]> { return this.http.get<Task[]>(this.apiUrl); } deleteTask(task: Task): Observable<Task> { const url = `${this.apiUrl}/${task.id}`; return this.http.delete<Task>(url); } addTask(task: Task): Observable<Task> { return this.http.post<Task>(this.apiUrl, task, httpOptions); } }
Creating the UI Service
The UI service will control the form-toggling functionality via the toggleAddTask
function.
//language: JavaScript import { Injectable } from '@angular/core'; import { Observable, Subject } from 'rxjs'; @Injectable({ providedIn: 'root', }) export class UiService { private showAddTask: boolean = false; private subject = new Subject<any>(); constructor() {} toggleAddTask(): void { this.showAddTask = !this.showAddTask; this.subject.next(this.showAddTask); } onToggle(): Observable<any> { return this.subject.asObservable(); } }
Great! Now that we’ve defined the services, now we move on to create the components.
Creating the header component
The header component will contain a button, which, when clicked, will toggle the form the user will use to add more tasks. In addition, the background color and text of the button will also be toggled.
To set this up in Angular, create a header component in the app and do the following:
First, define the HTML for the component.
//language: JavaScript <header> <h1>Task Tracker</h1> <app-button color="" text="" (btnClick)="toggleAddTask()" ></app-button> </header>
The button accepts a btnClick
handler and the toggleAddTask
method is responsible for toggling the form’s visibility.
Hook up to the service and create the toggleAddTask
method.
Then, add the code to define the behavior:
//language: JavaScript import { Component, OnInit } from '@angular/core'; import { Subscription } from 'rxjs'; import { UiService } from 'src/app/services/ui.service'; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.css'], }) export class HeaderComponent implements OnInit { showAddTask: boolean = false; subscription: Subscription; constructor(private uiService: UiService) { this.subscription = this.uiService .onToggle() .subscribe((value) => (this.showAddTask = value)); } ngOnInit(): void {} ngOnDestroy() { // Unsubscribe to ensure no memory leaks this.subscription.unsubscribe(); } toggleAddTask() { this.uiService.toggleAddTask(); } }
We access the UI service containing a toggleAddTask
and use that to define a second toggleAddTask
method, which we used in the header’s HTML.
We can see that there are quite a number of moving parts when it comes to setting up functionality for Angular. Let’s see how this works in React.
First, create a header component and paste in the code below.
//language: JavaScript import Button from "../Button/Button"; import styled from "styled-components"; import { useShowFormContext } from "../../context/showFormContext"; export default function Header() { const { showAddTaskForm, toggleAddTaskForm } = useShowFormContext(); return ( <StyledHeader> <h1>Task Tracker</h1> <Button bgColor={showAddTaskForm ? "red" : "green"} btnClickHandler={toggleAddTaskForm} btnLabel={showAddTaskForm ? "Close" : "Add"} /> </StyledHeader> ); }
Here, we access the showAddTaskForm
boolean and the toggleAddTaskForm
functions from showFormContext
and use them to set up the functionality. Next, we create the context.
Create the showFormContext
, which will contain showAddTaskForm
and toggleAddTaskForm
.
//language: JavaScript import { useState, useContext, createContext } from "react"; const ShowFormContext = createContext(); export const useShowFormContext = () => useContext(ShowFormContext); export default function ShowFormContextProvider({ children }) { const [showAddTaskForm, setShowAddTaskForm] = useState(false); const toggleAddTaskForm = () => { setShowAddTaskForm(!showAddTaskForm); }; return ( <ShowFormContext.Provider value=> {children} </ShowFormContext.Provider> ); }
We’ve set up the same component with similar features but with fewer lines of code — that’s a win for React and the simplicity it gives us.
Creating the button component
This button is not to be mistaken for the button on the form. Rather, it is the button we used in the header component. Let’s start with Angular.
Create the button’s HTML:
//language: JavaScript <button class="btn" (click)="onClick()" [ngStyle]="{ 'background-color': color }"> </button>
Set up the button’s functionality:
//language: JavaScript import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; @Component({ selector: 'app-button', templateUrl: './button.component.html', styleUrls: ['./button.component.css'], }) export class ButtonComponent implements OnInit { @Input() text: string = ''; @Input() color: string = ''; @Output() btnClick = new EventEmitter(); constructor() {} ngOnInit(): void {} onClick() { this.btnClick.emit(); } }
The color and text of the button are dynamic, so we set up variables for them. Furthermore, the button needs to accept a click event, so we emit the btnClick
.
Note how much more complex it is to define props and pass dynamic data to components in Angular.
Let’s set up the button in React and see how it looks:
//language: JavaScript import Styled from "styled-components"; export default function Button({ btnLabel, bgColor, btnClickHandler }) { return ( <StyledButton onClick={btnClickHandler} bgColor={bgColor}> {btnLabel} </StyledButton> ); }
This is all it takes! Clearly, Angular’s version of ‘props’ has a higher level of complexity.
Creating the Add Task Component
The Add Task component is the form we use to add new tasks. Let’s set it up in Angular.
Here’s how we define the HTML skeleton:
//language: JavaScript <form *ngIf="showAddTask" class="add-form" (ngSubmit)="onSubmit()"> <div class="form-control"> <label for="text">Task</label> <input type="text" name="text" [(ngModel)]="text" id="text" placeholder="Add Task" /> </div> <div class="form-control"> <label for="day">Day & Time</label> <input type="text" name="day" [(ngModel)]="day" id="day" placeholder="Add Day & Time" /> </div> <input type="submit" value="Save Task" class="btn btn-block" /> </form>
Here, we do the following:
- Define the HTML and use Angular’s
ngModel
form directive to track the value of the form fields - Conditionally render the form with the
*ngIf
directive based on the state ofshowAddTask
Make the form interactive
Next, we need to add functionality to the form by defining the variables the data will be passed to and handling the form submission:
//language: JavaScript import { Component, OnInit, Output, EventEmitter } from '@angular/core'; import { UiService } from 'src/app/services/ui.service'; import { Subscription } from 'rxjs'; import { Task } from 'src/app/mock-tasks'; @Component({ selector: 'app-add-task', templateUrl: './add-task.component.html', styleUrls: ['./add-task.component.css'], }) export class AddTaskComponent implements OnInit { @Output() onAddTask: EventEmitter<Task> = new EventEmitter(); text: string; day: string; showAddTask: boolean; subscription: Subscription; constructor(private uiService: UiService) { this.subscription = this.uiService .onToggle() .subscribe((value) => (this.showAddTask = value)); } ngOnInit(): void {} ngOnDestroy() { // Unsubscribe to ensure no memory leaks this.subscription.unsubscribe(); } onSubmit() { const newTask: Task = { text: this.text, day: this.day, }; this.onAddTask.emit(newTask); this.text = ''; this.day = ''; } }
Here, we create the onSubmit
function, which adds new tasks and clears the form field. We also add the necessary variables that will hold the form data.
Let’s set up the same thing in React:
//language: JavaScript const handleSubmit = (e) => { e.preventDefault(); const formData = new FormData(e.target); const data = Object.fromEntries(formData); console.log(data); }; export default function AddTask() { return ( <StyledForm className="add-form" onSubmit={handleSubmit}> <div className="form-control"> <label for="text">Task</label> <input type="text" name="task" id="text" placeholder="Add Task" /> </div> <div className="form-control"> <label for="day">Day & Time</label> <input type="text" name="day" id="day" placeholder="Add Day & Time" /> </div> <input type="submit" value="Save Task" className="btn btn-block" /> </StyledForm> ); }
Let’s break down the code above:
- First, we define the HTML
- Next, we set up the
handleSubmit
function, which leverages the FormData constructor to access the form’s data
Creating the task item and task components
The task item component is each task that is performed. It contains the task’s title, date, and a button for deleting the task.
Let’s set it up in Angular:
//language: JavaScript <div class="task"> <h3> <fa-icon [icon]="faTimes" [ngStyle]="{ color: 'red' }" (click)="onDelete(task)" ></fa-icon> </h3> <p></p> </div>
We need to create the onDelete
method that we passed to the icon.
//language: JavaScript import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; import { Task } from 'src/app/mock-tasks'; import { faTimes } from '@fortawesome/free-solid-svg-icons'; @Component({ selector: 'app-task-item', templateUrl: './task-item.component.html', styleUrls: ['./task-item.component.css'], }) export class TaskItemComponent implements OnInit { @Input() task: Task; faTimes = faTimes; @Output() onDeleteTask = new EventEmitter<Task>(); constructor() {} ngOnInit(): void {} onDelete(task: Task) { this.onDeleteTask.emit(task); } }
Here, we define the onDelete
method and use that to omit the onDeleteTask
method, so the Task Item’s parent can catch the delete event and remove the task.
Let’s create the Tasks component to see what that looks like.
//language: JavaScript <app-add-task (onAddTask)="addTask($event)"></app-add-task> <app-task-item *ngFor="let task of tasks" [task]="task" (onDeleteTask)="deleteTask(task)" ></app-task-item>
Here, we define the HTML for the Tasks component. There, we loop through the array of tasks and render each task in the Task Item component.
Then, we catch the onDeleteTask
delete event from the Task component and use that to set up the task deletion functionality. We also do the same thing for the Add Task component and the onAddTask
event.
Finally, we utilize the functionality of the Tasks service:
//language: JavaScript import { Component, OnInit } from '@angular/core'; import { Task } from 'src/app/mock-tasks'; import { TaskService } from 'src/app/services/task.service'; @Component({ selector: 'app-tasks', templateUrl: './tasks.component.html', styleUrls: ['./tasks.component.css'], }) export class TasksComponent implements OnInit { tasks: Task[] = []; constructor(private taskService: TaskService) {} ngOnInit(): void { this.taskService.getTasks().subscribe((tasks) => (this.tasks = tasks)); } deleteTask(task: Task) { this.taskService .deleteTask(task) .subscribe( () => (this.tasks = this.tasks.filter((t) => t.id !== task.id)) ); } addTask(task: Task) { this.taskService .addTask(task) .subscribe((task) => this.tasks.unshift(task)); } }
Here, we access the addTask
and deleteTask
functions from the service and use them to create another set of addTask
and deleteTask
methods that we used in the Tasks and Task Item components.
Then, we fetched the list of tasks from the mock API using the getTasks
function.
That’s it for Angular! Now, let’s migrate the Tasks and Task Item functionality to React, starting with Task Item:
//language: JavaScript export default function TaskItem({ task, tasks, setTasks }) { const deleteTask = () => { let newTasks; fetch(`http://localhost:5000/tasks/${task.id}`, { method: "DELETE", }) .then((newTasks = tasks.filter((t) => t.id !== task.id))) .then(setTasks(newTasks)); console.log("red"); }; return ( <Container className="task"> <h3> {task.text} <DeleteIcon const deleteTask={deleteTask} /> </h3> <p>{task.day}</p> </Container> ); } function DeleteIcon({ deleteTask }) { return ( <svg onClick={deleteTask} > <path d=""></path> </svg> ); }
Here, we defined a deleteTask
function and passed it to the icon. Then we also rendered the data for the task.
For the Tasks component:
//language: JavaScript import AddTask from "../AddTask/AddTask"; import TaskItem from "../TaskItem/TaskItem"; import { useShowFormContext } from "../../context/showFormContext"; import { useState, useEffect } from "react"; export default function Tasks() { const [tasks, setTasks] = useState(null); const { showAddTaskForm } = useShowFormContext(); useEffect(() => { fetch("http://localhost:5000/tasks") .then((res) => res.json()) .then((data) => setTasks(data)); }, []); return ( <> {showAddTaskForm && <AddTask />} {tasks ? tasks.map((task) => ( <TaskItem key={task.id} task={task} tasks={tasks} setTasks={setTasks} /> )) : null} </> ); }
Here, we did the following:
- Fetched the tasks from the mock API
- Toggled the visibility of the form based on the value of
showAddTaskForm
- Rendered the list of tasks
With that, we have successfully converted a basic task tracker app from Angular to React.
However, note that this was a basic project and that migration will be more complex when dealing with Angular apps with multiple services, complicated state management, and many pages and features — but the principle remains the same.
Insights from the conversion process
- While Angular and React are both great frontend frameworks, the former is more complex than the latter
- On average, it takes significantly more lines of code to create a feature in Angular than it does in React
- Overall, converting components from Angular to React results in fewer lines of code and makes things simpler and faster. This improves app performance and provides a better maintenance and developer experience moving forward
Conclusion
Migrating can be a rather daunting task that takes a lot of effort, time, planning, and development to ensure it goes smoothly without compromising maintenance and development speed. It is important to consider the pros and cons before migrating.
While there are many benefits of converting with a total rewrite, it isn’t always possible because the majority of companies don’t have the time or resources to commit to this method. In such cases, we can leverage tools that automate a large chunk of the migration process, such as ngReact, react2angular, and angular2react.
I hope you found this explainer and tutorial article useful — you can get the full source code for the Angular and React app from this repo.
The post The guide to converting Angular apps to React appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/NsI9tcq
Gain $200 in a week
via Read more