How to convert an AngularJS 1.x app to a React app — one component at a time

AngularJS and React are both popular JavaScript frameworks for building web applications, but they represent different eras of web development. AngularJS (version 1.x) was first released in 2010 and provides a full-featured MVC framework for building complex single-page applications. React, on the other hand, is a more modern library (launched in 2013) that focuses solely on the view layer, using a component-based architecture and a virtual DOM for efficient rendering.

There are many reasons why you might want to migrate an AngularJS 1.x application to React:

  • React‘s component-centric model is easier to reason about and promotes better code reuse
  • React‘s virtual DOM implementation is very performant, especially for larger apps
  • The React ecosystem is actively developed and includes many great tools for development and testing
  • Aligning with React can make it easier to tap into the large React community and talent pool

However, for large AngularJS applications, rewriting the entire codebase from scratch in React is often not feasible. It would be very time-consuming, disruptive to new feature development, and risky to deploy as a "big bang" release. A better approach is to incrementally migrate the application component-by-component, allowing the two frameworks to coexist in a hybrid app during the transition period.

Setting up a hybrid AngularJS+React project

The first step to enabling an incremental migration is to set up your project to support running both AngularJS and React code together. To do this, you‘ll need to:

  1. Install the necessary dependencies:

    • React and ReactDOM for the React components
    • Babel to transpile JSX and modern JS syntax
    • Webpack (or another build tool) to bundle everything together
  2. Configure your build system. The key parts are:

    • Enabling both .js and .jsx file extensions
    • Transpiling both AngularJS and React code with Babel
    • Bundling the app with both AngularJS and React entry points
  3. Decide how to share data between the two frameworks. Some options are:

    • Using a shared set of AngularJS services that both Angular and React components depend on
    • Adopting a state management library like Redux that can be used by both
    • Passing data and callbacks between Angular and React explicitly as React component props

Here‘s an example of what a minimal hybrid AngularJS+React project might look like:

├── package.json
├── webpack.config.js
├── src
│   ├── app.js     // AngularJS app module bootstrap
│   ├── index.js   // React app entry point 
│   ├── components
│   │   └── ...    // Shared AngularJS components and services
│   └── views 
│       ├── ...    // AngularJS route templates
│       └── react  // Directory for React components

With this setup, you‘re ready to start converting AngularJS components to React one at a time while keeping the app fully functional.

Converting an AngularJS component to React

Let‘s walk through an example of converting a specific AngularJS 1.x component to React. Suppose we have a UserProfile component that displays a user‘s avatar, name, and bio. In AngularJS, it might look something like this:

// user-profile.component.js
angular.module(‘app‘).component(‘userProfile‘, {
  templateUrl: ‘user-profile.html‘,
  bindings: {
    user: ‘<‘
  },
  controller: function UserProfileController() {
    this.isEditing = false;

    this.startEditing = () => {
      this.isEditing = true;
    };

    this.saveBio = (newBio) => {
      this.user.bio = newBio;
      this.isEditing = false;
    };
  }
});
<!-- user-profile.html -->
<div class="user-profile">
  <img ng-src="{{$ctrl.user.avatarUrl}}" />
  <h2>{{$ctrl.user.name}}</h2>

  <div ng-if="!$ctrl.isEditing">
    <p>{{$ctrl.user.bio}}</p>
    <button ng-click="$ctrl.startEditing()">Edit Bio</button>
  </div>

  <div ng-if="$ctrl.isEditing">  
    <textarea ng-model="$ctrl.user.bio"></textarea>
    <button ng-click="$ctrl.saveBio($ctrl.user.bio)">Save</button>
    <button ng-click="$ctrl.isEditing = false">Cancel</button>
  </div>
</div>

To convert this to a React component, we would:

  1. Create a new React component file (UserProfile.jsx) with the same basic structure as the AngularJS version:
// UserProfile.jsx
import React, { useState } from ‘react‘;

function UserProfile(props) {
  const [isEditing, setIsEditing] = useState(false);
  const { user } = props;

  function startEditing() {
    setIsEditing(true);
  }

  function saveBio(newBio) {
    user.bio = newBio;
    setIsEditing(false); 
  }

  return (
    <div className="user-profile">
      <img src={user.avatarUrl} alt={user.name} />
      <h2>{user.name}</h2>

      {!isEditing && (
        <>
          <p>{user.bio}</p>
          <button onClick={startEditing}>Edit Bio</button>  
        </>
      )}

      {isEditing && (
        <>
          <textarea value={user.bio} onChange={e => user.bio = e.target.value} />
          <button onClick={() => saveBio(user.bio)}>Save</button>
          <button onClick={() => setIsEditing(false)}>Cancel</button>
        </>  
      )}
    </div>
  );
}

export default UserProfile;
  1. Update the AngularJS template to render the React UserProfile component instead of the original AngularJS version:
<!-- user-profile.container.html -->
<div>
  <react-component name="UserProfile" props="{user: $ctrl.user}" watch-depth="reference" />
</div>
  1. Update any AngularJS components or services that use userProfile to instead use the new UserProfile:
// some-feature.component.js
import UserProfile from ‘./UserProfile.jsx‘;

angular.module(‘app‘).component(‘someFeature‘, {
  template: `
    <div>
      <user-profile user="$ctrl.user"></user-profile>
    </div>  
  `,
  controller: function SomeFeatureController(User) {
    this.user = User.get(123);
  }
});

By applying this component-by-component conversion process throughout the application, you can eventually move the entire app over to React. The key is to do it in small, incremental steps, always keeping the app in a releasable state.

Challenges and pitfalls

While an incremental migration can be a great approach, it‘s not without its challenges. Some things to watch out for:

  • Performance overhead of running both frameworks simultaneously. Both AngularJS and React have their own change detection cycles that will be running, so be mindful of potential slowdowns.

  • Complexity of sharing state. Whatever method you choose for synchronizing data between Angular and React can become tricky to maintain over time, especially if there are circular dependencies.

  • Keeping the build configuration manageable. As you convert more of the app to React, your Webpack (or other bundler) configuration can become unwieldy with too many special cases. Try to keep it clean and consider breaking it into multiple builds if needed.

Best practices for a smooth migration

Here are some tips to keep in mind as you embark on an AngularJS to React migration:

  1. Start with low-hanging fruit. Begin by converting smaller, simpler components that have few dependencies on the rest of the app. This will help you get comfortable with the process before tackling more complex components.

  2. Have a clear data-sharing strategy. Decide early on how you will pass data between the two frameworks and stick with it. Avoid mixing multiple approaches unless absolutely necessary.

  3. Continuously refactor. As you convert components, take the opportunity to also clean up and modernize the code. Don‘t just blindly port things over.

  4. Write lots of tests. Automated tests are critical during a migration to avoid regressions. Write unit tests for your React components and integration tests for the touchpoints between Angular and React.

  5. Use a compatibility library. Tools like react2angular or angular2react can make integrating React and Angular components easier by handling the wiring between the two.

  6. Go all-in on React. While it may be tempting to keep some parts in AngularJS indefinitely, you‘ll get the most benefit by fully converting to React. Set a clear end-date for the migration and work towards it.

Conclusion

Converting an AngularJS 1.x application to React one component at a time is a great way to modernize a codebase without ditching your current app. By chipping away at it piece-by-piece, you can eventually move the entire app over to React and reap the benefits of a modern component architecture, virtual DOM, great tooling, and active community.

The key to success is planning and patience. Map out your component conversion strategy, put the right infrastructure in place to enable a hybrid app, and then methodically work through the process. Don‘t rush things, keep the app stable, and continue to deliver value while you migrate.

The end result will be a more maintainable, performant, and future-proof application. One that lets you tap into the thriving React ecosystem while still leveraging the hard work you‘ve already put into your AngularJS app. Happy migrating!

Similar Posts