Migrating Angular JS app to React
We often have to deal with legacy code at some point. In my organization, we have an app that uses the AngularJS framework and is not touched for quite some years. Recently, we were tasked to add a new feature to that app.
The app had quite a lot of outdated dependencies with dependencies coming from both
package.json. The build tool used older versions of
We tried to add the new feature without upgrading any of the packages, but it resulted in a lot of pain. Even a tiny change to the old AngularJS framework was tiresome.
Upgrading to the latest version of Angular required a complete rewrite of the entire app. We all know rewriting an entire application needs a lot of man work. So, we decided to look for options that offer an incremental upgrade.
All the apps in my organization use React. Hence, we decided to move forward to introduce React to this project. I took the responsibility to do it. After some research on Google, some libraries like ngReact, react2angular, angular2react offer migration to React from legacy Angular JS code.
Since the app uses
v1.2.28 of AngularJS,
ngReact is the only package that offers support as other packages are targeted for higher versions.
Now, let’s see how to introduce React + Typescript into an Angular JS app using
ngReact along with tools like Prettier, ESLint etc.,
This can be done in a series of following steps:
- Install dependencies
- Webpack configuration
- Adding tsconfig.json
- Adding ESLint config
- Loading dependencies
- Adding React component
Install the necessary dependencies. The demonstration uses
yarn for dependency inclusion.
Add the following as
yarn add typescript @types/react @types/react-dom @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-config-prettier eslint-plugin-react eslint-plugin-react-hooks prettier -D
Add the following as
yarn add react react-dom ngreact
Add the necessary dependencies for webpack.
yarn add ts-loader fork-ts-checker-webpack-plugin -D
The project used a combination of Gulp and Webpack for bundling. As a first step, I upgraded all the dependencies of Gulp and Webpack. Since removing Gulp needed a lot of effort, we decided to remove it at a later stage.
Adjust the webpack configuration as follows:
As you can see in the above example, the ts-loader package manages the files with extension .ts and .tsx. It is also advised to include the transpileOnly option to true so that fork-ts-checker-webpack-plugin takes care of type checking.
If you use AngularJS < 1.3.14, it is also important to add the corresponding loader as seen in the snippet to recognize `require(‘angular’)`
To enable Typescript, add the tsconfig.json with
allowJs option set to
jsx option set to
react-jsx. Enabling this option, allows you to skip React import in every file for newer versions of React.
Adding ESLint config
Create a .eslintrc file and add the necessary plugins of your choice like below. To avoid typescript, linting js files, apply
@typescript-eslint plugin only for files with extension .ts and .tsx in
overrides section as seen in the snippet.
Create a vendors.js file and include high-level dependencies.
ngReact requires loading React and AngularJS dependencies before its inclusion as below.
Including react directive
In your root module, include
react as the first module before all others as follows:
Adding a component
Now with all the setup done, let’s add our first good old hello world react component.
Before creating the components, we need an additional step to convert the React component into an AngularJS directive.
These three lines of code do the conversion. This function takes the React component and the array of property strings as arguments and converts it to an angular directive.
Create a HelloWorld.tsx file as below.
Pass the component and the props as an array
["name"] and export the return value from the
createAngularDirective as provided in the snippet above.
Creating Angular Directive
Within the Angular JS module, embed the
React directive like below. Now, one could access the
<hello-world> directive in the AngularJS HTML file.
Using the directive within HTML
Here, the value
"name" comes from AngularJS scope, i.e.
$scope.name. This way, one can use React component within the AngularJS module and pass props as well.
Navigation between routes
One could use window.history.pushState in the React component to navigating to the respective AngularJS route.
Husky hooks allow prettifying files.
yarn add husky pretty-quick -D
Add the following script under scripts in package.json and run it only once using the command
yarn prepareafter installing husky.
"prepare" : "husky install"
Then create a pre-commit hook as:
npx husky add .husky/pre-commit "yarn pretty-quick -- staged"
This creates necessary files for the hook and runs prettier for staged files at the time of commit.
More info on Husky v6 can be found here.