Moving from Angular to React – 100% done

Moving from Angular to React – 100% done

In 2017, Bokio was built in Angular. We had to choose between upgrading Angular to version 2 or choose something else. We decided to go with React. A lot has happened since then and we will try to tell you about our migration from Angular to 100% React.

Angular

Bokio was built with Angular as front-end with many thousand lines of code – what a waste to remove it? Yes, that is true, but we had to do something.

At this time in 2017, Bokio was growing its user base about 10% per week, yes per WEEK! So it was a tough decision to go for something new, React.

Here is some code that is still in our git repo (hopefully soon removed).

var module = angular.module('Bokio.Verifications');
module.directive('ocrImage', ['$timeout', function($timeout) {
  return {
    restrict: 'E',
    transclude: false,
    replace: true,
    templateUrl: '/Areas/Accounting/App/Directives/ocrImage.html',
    scope: {
    recieptData: '=recieptData',
  },
  controller: ['$scope', '$http', '$rootScope', function($scope, $http, $rootScope) {
    $scope.Lang = window.Lang;
    $scope.imageSrc = function() {
    return $scope.recieptData && $scope.recieptData.Reciept ? '/' + $scope.recieptData.Reciept.CompanyId + '/Accounting/Receipt/ShowReciept/' + $scope.recieptData.Reciept.Id + "?v=" + $scope.recieptData.Reciept.Version : '';
  };
  $scope.ocrImageLoaded = function(data) {
    $rootScope.$broadcast('ocrImageLoaded', data);
  };
  }],
  };
}]);

Hard to find bugs in Angular

As I remember it, it was quite hard do find bugs in angular. We logged all browser errors to Stackify, but it was still really hard to narrow down some of the bugs. This was one of many reasons we thought about moving away from Angular. It was also 7 years old, since first release.

I found a screenshot in a Slack message from mid 2016: [$rootScope:infdig] http://errors.angularjs.org/1.3.15/$rootScope/infdig?p0=10&p1=%5B%5D

Angular error in Bokio

What options did we have?

  • Stay with Angular 1
  • Upgrade to Angular 2
  • Move to React
  • Move to something else

Upgrading to Angular 2 would be a major overhaul, which we deemed almost as hard as moving to React or any other framework. We came to a decision Move to React.

Migration path to React

We started by porting all existing code we had in the Salary part of the Bokio product to React. We chose to start with one of the smallest parts of the Bokio product, both in terms of number of lines of code and usage by our users.
Our strategy was pure porting, meaning we would only keep the same functionality. We did this in order to enable easy comparisons between the old Angular version and the new React version.

This took a couple of months until we had something we could test internally.

After a while we thought we had a version stable enough to release to all users. For sure there were bugs, but we could fix them rather quickly.

Feature toggles for accounting and invoicing

Later on we decided to move the accounting and invoicing parts of the product to React. In these areas we didn't port the functionality, we actually changed a lot but under feature toggles.

We use feature toggles to try the new versions with some new users and then get feedback.

Here are some of the old feature toggles we have had on our way to React.

NewVacation = 1, // ME: 2017-11-01
NewTodo = 2, // SC: 2017-01-09
ReactBookkeeping = 3, // EH: 2018-01-19
ReactSupplierInvoices = 4, // EH: 2018-01-30
ReactBalanceReports = 6, // NE: 2018-02-06
ReactResultReport = 8, // NE 2018-03-22
ReactClosure = 9, // MQ: 2018-03-22
ReactLedger = 11, // MQ: 2018-05-23
ReactAccounted = 12, // MQ: 2018-06-25
ReactDashboard = 14, // NE: 2018-09-17

2020 and we are done

We were almost done in end of 2018, but some code never got prioritized to be migrated.

But now, after three years, we are finished with moving all code from Angular to React. What a journey!

Let's celebrate with some React code:

<div className={styles.extraPagesContainer}>
  {receiptPages.map(page => (
    <ReceiptPageThumbnail
      key={page.imageSrc}
      src={page.imageSrc}
      selected={selectedPage.imageSrc === page.imageSrc}
      onClick={() => setSelectedPage(page)}
    />
  ))}
  <ReceiptPageThumbnail as="div">
    <AddReceiptExtraPageButton
       className={styles.addPageButton}
      label={<Icon name="plus" />}
      receiptId={receipt.Id}
      saveAsTemporary={true}
      pageAdded={onAddNewPage}
    />
  </ReceiptPageThumbnail>
</div>