Gradually Migrating from AngularJS to React + Redux

Part 1 — Making React Work in an AngularJS App

Guy Perkal
BigPanda Engineering
4 min readApr 22, 2018

--

In BigPanda, we have been using AngularJS for a long time, and over time we ran into the same issue a lot of software projects do - the bigger our codebase got, the harder it was to maintain. Granted, it wasn’t all AngularJS’s fault — a lot of our problems were caused by a lack of clear design principles and best practices, which over time resulted in tangled, coupled code that was very fragile and difficult to test.

One of our biggest problems was the issue of state management, specifically sharing a state between multiple components in a way that didn’t strongly couple them. Without a clear recipe, we ended up with a mishmash of $scope.$watch, $scope.$broadcast and shared in-memory caches kept in AngularJS services. Over time we came up with our own Redux-like state management architecture, which helped when writing new features, but maintaining older areas of the code was still painful. This, combined with some performance issues and the fact that AngularJS was no longer being actively developed, made us look into whether a complete rewrite would be feasible.

Since we’re a fast moving startup and couldn’t afford to stop working on features while we rewrite everything, we needed to find a way to do a gradual migration, so that new features could be written in some new framework and existing features could be slowly replaced over time. As far as framework options, we looked into Angular, React and Vue, and after a lot of research and discussion we decided React + Redux was the best fit for us.

So now we needed to find out: could we run Redux-connected React components in AngularJS apps, and do it in a way that will allow us to eventually get rid of all legacy AngularJS code? For this, there were a few problems we needed to find a solution for:

Inserting React components into AngularJS templates

For this, we used a helpful third-party library called react2angular. This lets us wrap our React components in AngularJS components, so that they can be used seamlessly in any of our AngularJS templates. This is what it looks like:

import { react2angular } from ‘react2angular’;
import UserMenu from ‘react/layout/topbar/components/UserMenu';
angular.module(‘bigpanda’).component(‘userMenu’, react2angular(UserMenu));

Passing our Redux store to React components

Ok, so we can display React components in AngularJS, but how can we pass the Redux store to them? In classic React-Redux apps the recommendation is to use a Provider component to wrap the root component, which makes sure the store is passed to the context of every child component. In a hybrid app like ours, this wouldn’t work since there isn’t one root React component, and wrapping each component with a provider separately would be too boilerplate-y. Instead, we decided to use ng-redux, so that it’s AngularJS’s responsibility to pass the store to the React components:

import { rootReducer, rootEpic } from '../../react';angular.module('bigpanda').config(($ngReduxProvider) => {
const epicMiddleware = createEpicMiddleware(rootEpic);

$ngReduxProvider.createStoreWith(
rootReducer,
[epicMiddleware]
);
}

And now our previous example looks like this:

import { react2angular } from ‘react2angular’;
import UserMenu from ‘react/layout/topbar/components/UserMenu;
angular.module(‘bigpanda’).component(
‘userMenu’,
react2angular(UserMenu)
);
angular.module('bigpanda').controller('UserMenuCtrl', UserMenuCtrl);
function UserMenuCtrl($ngRedux) {
this.store = $ngRedux;
}

And so now we can send the Redux store as a property to our React components.

Routing to React components

The last step is routing directly to React components. We use UI-Router, so our first instinct was to use react-hybrid. Unfortunately, this project is in its early stages and was too buggy for us, so instead react2angular proved useful yet again -

import { react2angular } from 'react2angular';
import PersonalSettings from './components/PersonalSettings';
angular.module('bigpanda').component(
'personalSettings',
react2angular(PersonalSettings)
);
export const appSettingsPersonal = {
url: '/personal',
views: {
'settings-page-view': {
template: '<personal-settings store="store" />',
controller: ($scope, $ngRedux) => {
$scope.store = $ngRedux;
}
}
}
};

What we’re doing is wrapping the React component in an AngularJS component, and then we route to an inline-template that uses that AngularJS component.

So now that we knew it was possible to get a hybrid AngularJS + React-Redux app working, we needed to decide on how to structure our new React-Redux code in a way that will allow us as painless a migration as possible, while also making sure not to mix the React-Redux code with the AngularJS code. I’ll discuss this in my next post.

--

--