Moving from Angular to React

MB

November 13, 2018

Published by Matthew Beasley

Bright HR SVG

React or Angular?

For the last 10 months we've been migrating our Angular 1 app to React. We've just hit a milestone of over 50% of the codebase being React so we thought we'd share how we got here.

Why Move?

Angular 1 is not in active development, our implementation is outdated, some of the highlights...

  • Not all code is tested, that which is was retroactively tested which makes the tests less trustworthy
  • Files with over 3000 lines, parameter mutations, lot's of repeated code and HTML (your basic code smells).
  • So many linting problems we just had to turn a bunch of rules off - we like AirBnb's.
  • Angular DI puts a tax on breaking code up.

Why React?

There are two sensible choices for a SPA, Angular or React - I know there are others but you'd have to have a very convincing argument to not use one of the two heavily supported options. In my experience developers pick React up much quicker, all the core examples are in ES6 not typescript, and more developers in the local market want to code in React. The latest figures from JS survey back this up. I use this well respected survey as a more general guide. Developers who've used React are far more likely to want to use is again.

How we moved

Moving to new architecture should always be done in a safe iterative manner, reasons include...

  • A big bang release of a replacement site is so risky - you get no feedback until you've 'finished'.
  • The business is growing and still needs to add new features.
  • It allows priorities to change; if a major new piece of work comes in we can move onto it immediately.

Grunt to Webpack

React without Webpack (or something similar) would be very difficult to implement. Our existing app used Grunt tasks to create a build. Before we could write any React we had to convert the whole build process to Webpack with no functional changes. This should not be underestimated, Grunt and Webpack look at a codebase in a totally different way - things which are simple in one are complex in another and vice versa. Our config could cover a blog post in itself...

Implement a component library

React encourages reuse of components, we're going to solidify this further by using Styleguidist to produce living documentation and keep the components in a separate repo; here's a link to our documentation.

Start writing React in Angular!

In order to deliver changes in small iterative chunks we need a number of techniques to cover each scenario - we've found three...

Mounting a React component from Angular 1 router

The router is the heart of the a SPA, it's very hard to get Angular 1 working with a React router, so we made the pragmatic decision we would replace that last. If the React we're building is for a complete page we use the following trick to render a React component.

.state('reports', {
    url: '/reports',
    template: '<div id="reports"></div>',
    controller() {
        ReactDOM.render(<Reports />, document.querySelector('#reports'));
    }
}

We create an inline Angular template which will host the React, we use the controller callback to render our shiny new React code.

What if we need to get parameters from the URL to drive behaviour...

.state('view-employee-record', {
    url: '/employee/:id/record/:recordId',
    template:
        '<div id="toil-earned"></div>',
    controller($stateParams) {
        const { id, recordId } = $stateParams;
        ReactDOM.render(
            <Record employeeId={id} recordId={recordId} />,
            document.querySelector('#toil-earned')
        );
    }
}

We can access Angular’s $stateParams within the controller which we can then pass as props into our React component

Mounting and unmounting within Angular JS

We wanted to replace modal screens in a section of our site with React, this meant mounting inside Angular javascript.

ReactDOM.render(
  <ConfirmationModal
    onCancel={() => {
      ReactDOM.unmountComponentAtNode(
        document.getElementById("ConfirmationModal")
      );
    }}
    onConfirm={() => {
      deleteDocument(doc);
      ReactDOM.unmountComponentAtNode(
        document.getElementById("ConfirmationModal")
      );
    }}
  >
    Are you sure you want to delete {doc.name}.
  </ConfirmationModal>,
  document.getElementById("ConfirmationModal")
);

Because modals are temporary they need to be responsible for their own unmounting to keep things clean. To do this we mount in the usual way and use unmountComponentAtNode to tidy up when the modal is closed.

Mounting React from Angular templates

The majority of our React is part of Angular templates, we can use ngReact to mount React directly from within templates.

We register the component as a directive - this is a standard React component

import EmployeeInformation from "components/Absence/EmployeeInformation";

angular
  .module("brightApp.directives")
  .value("EmployeeInformation", EmployeeInformation);

We can then mount a component from a template using ngReact referencing the component name registered.

<react-component
  name="EmployeeInformation"
  props="{employeeId: ctrl.$stateParams.id}"
/>

Here we're passing employeeId as a prop using $stateParams object available from the angular controller.

How are we doing?

How do we measure how we are doing? All the devs here are very keen to do nothing but React but it's still good to have a reminder and targets to aim for.

I build a simple node script which uses Git to checkout the codebase each week and record the amount of lines of Angular and React. I then plot that as a graph which I put on our Slack channel. Here's the latest one....

Angular vs React

Registered Office: Bright HR Limited, The Peninsula, Victoria Place, Manchester, M4 4FB. Registered in England and Wales No: 9283467. Tel: 0844 892 3928. I Copyright © 2024 BrightHR