Angular is Easy. React is Hard.

I know exactly what you’re thinking…

Blasphemy! React is easier to reason about! — The Internet

This is the same reaction I get when peers ask for advice on which library or framework to use, whether or not to scrap their prototype & rebuild it, or to help convince a coworker or boss to learn something new.

Of course, this isn’t universally true, but I love generalities, and this is generally consistent with my experience.

So my words have some merit, keep in mind that I have some experience with Angular & React both:

This isn’t an eloquent dialogue (though I’ve been playing Devil’s Advocate in my mind throughout this writing), but is a synopsis of my personal reasons behind my blasphemous generalization.

So, with that, I’d like to offer some…


Perspective

I’m entirely ignoring what it takes to “get started” (e.g. setting up Webpack, Gulp, Grunt, LiveReload/BrowserSync, Yeoman, etc.). Besides, this changes daily.

Instead, I’m focusing on the impressions left from starting to, primarily, maintaining both Angular & React applications, which is where the vast majority of time seems to be spent for a seasoned developer.

(In other words, everything after the “honeymoon” period.)


Angular is Easy.

The largest Angular application I’ve built & maintained is a company-wide admin (of course).

No, this isn’t a simplistic, CRUD-like admin (e.g. Django Admin). Instead, it favors bulk management, per-page workflows, keyboard shortcuts, and roles/auth to restrict certain views & actions.

So what made Angular so easy for this?

Presentation & Logic Are Coupled

Going from zero to a functional prototype is insanely quick, especially when using frameworks like Foundation & Bootstrap. The code you copy & paste from examples or previous experiments work as expected, and you’re usually an ng-repeat or ng-if away from things working as expected.

As a result, there is minimal context switching and you’re simply working with data & presentation the vast majority of time. Things appear on the screen and they work!

Data Binding Makes Complex Apps Simple

I know several have made the counter-argument:

…but you don’t know who is actually mutating the data!

In my experience, this has never been a problem. Or, if it has, it has been such a minuscule one that I forget that it only hindered me for what must have been a few minutes.

You see, understanding how an Angular app works usually boiled down to a few steps:

  1. Find the relevant route from the URL, usually in routes.js.
  2. Open the relevant view, and check the view for the logic.
  3. If the view logic references a function, check the accompanying controller in routes.js.

That’s usually it. New members of the team, including those inexperienced with Angular, are usually able to “guess” what the pertinent file would be called, update the HTML with an ng-bind, commit, push, & deploy.

At the end of the day, we only had two spots where data was mutated: the view or the controller, both of which exceptionally easy to find & understand.

In fact, the sharing of $scope to child views meant that we largely ended up with a single root state that was available throughout the app.

Architecture is Solved

Angular provides a comprehensive amount of structure and solutions to most applications.

Most questions of “where does this thing go?” have an obvious answer that’s usually $scope, a controller, a service, or a directive.

Sure, there is still confusion as to what deserves a service vs. a factory, but most of the time this only comes into play when DRY-ing up code.

Angular certainly feels as if there’s a place for everything, and all that you have to bring to the table is a folder structure that makes sense to you.

React is Hard.

Most of this is admittedly due to React’s infancy outside of Facebook/Instagram. Nevertheless, this impression is largely concrete given the preceding examples of ease maintaining Angular projects.

JSX is Not Really HTML

I like JSX. I really, truly do! However, in regards to maintenance, building features, and on-boarding developers not familiar with JSX, it poses a bit of a hinderance.

  • Migrating existing code from other projects requires find/replace of className, for, and various other attributes.
  • Whitespace within {} expressions doesn’t behave how you’d expect.
  • You’re surprised how often you actually need to dangerouslySetInnerHTML.

Because JSX describes the Virtual DOM (and is not actual markup), there is often small bugs that exist from our years of coding HTML. Maybe there will be a Babel plugin for this one day…

Unidirectional Data Flow Is Complicated

With Redux, Alt, and Fluxible, I suppose this is “solved”, but still a great deal confusing for ongoing project maintenance.

In fact, last year, I explicitly chose Angular over React because triggering renders to parent & sibling components was so misunderstood and confusing. This is extremely evident in early experiments with React because they’re littered with Backbone models & this.forceUpdate() to make the UI “react” to changes.

Variations of Flux rely on context, the closest thing to Dependency Injection (or a Registry) that exists in React, which is rightfully still undergoing API changes, preventing any publicizing of this functionality.

As a result, solving React’s UI update problem required leveraging unknown APIs, or extremely new libraries, and a lot of architecture changes to match.

Even when a solution was chosen (I myself bounced around between Reflux, Alt, Flummox, and finally a custom solution eschewing Flux’s dispatcher), there was a cognitive leap to go from route to component to action to store and understand who was watching what for changes.

Props vs. State

One of the first cognitive roadblocks when maintaining an application (or adding new features) is what belongs in props and what belongs in state?

It’s easy now to declare that almost everything should be a prop, unless it’s directly related to the internal state of a component. But, when originally passing related down multiple levels of a tree meant polluting every component in-between with grandchildrens’ props, it was easy to make the case for individual components to manage their expectations internally with state.

Luckily, when pairing React with a good Flux implementation, dynamically injecting props can be a @decorator away.

Async Rendering

This is something I personally had to solve via React Resolver, but seemed counter-productive to me to assume that a, on the server in particular, all data the view needs to render has to be known upfront.

Even today in my own projects, rendering a full, complex component graph requires careful orchestration to ensure each dynamic dependency loads, renders, and completes successfully before sending the response to the client.

The only alternative that I’ve found outside of my own React Resolver is to treat route handlers as React’s version of Controllers: have them be intimately aware of all child components’ data needs & fetch them before doing a final, single, synchronous render.

The amount of bootstrapping & effort this takes is burdensome today, with no real “right” way of doing it.

BYOA (Bring Your Own Architecture)

As illustrated in the previous points, it all boils down to React only solving the View layer. However, it is abundantly clear that the ecosystem is entirely responsible for React-friendly solutions to all other layers.

For example, though I did this initially, I would not recommend using Backbone models within a React project. Nor would I recommend using something other than React Router for routing. Similarly, I would recommend using a community-sanctioned Flux implementation over integrating a random pub-sub library.

Why? Because React’s performance benefits around carefully crafted components require architecture that supports precise, performant updates, such as Immutable.js.

Mixing React with a library that is counter to React’s architectural expectations means hitting performance issues & quirks much sooner.

As a result, much of a React application is everything outside of the React component that you’re responsible for. Therefore, an Angular application with “fire and forget” data-binding can’t easily be translated into a React application, as the architecture around the View is fundamentally different.

Fear of the Status Quo

Solutions that still work for the majority of projects are avoided in favor of far-off solutions that have yet to be proven.

A recent example is the interesting thought experiment regarding CSS in JS. The explanations are clear, but I have never been in a situation or a team that has experienced the CSS dilemmas that warrant the burden of deriving solutions in JS.

Yet, the community has eagerly taken to inline-styling like a clarion call, solving a problem that other communities were blissfully unaware of.

Best practices are in constant flux within the React ecosystem, whereas all non-React projects I’ve worked on have evolved incrementally & predictably.

Conventional solutions are constantly being viewed through React’s lens. Though we’re better for it, it nevertheless creates an unstable environment for an application where the View layer creates a ripple through the entire infrastructure, giving pause to every piece of its implementation.

Ok. But Which is Better?

Short answer?

  • If prototyping, Angular.
  • If building a universal application, React.

Long answer:

  • Async rendering is largely solved via React Resolver.
  • Data-coupling is largely solved via Redux & Alt.
  • You will hit Angular performance issues much sooner than you expected. React will get you much, much further before you need to optimize.
  • Components are simpler & faster than Directives. Oddly enough, having more components clarifies an application’s architecture!
  • Rendering the same application on the server ensures a solid client-side experience by design.

Although, once Angular 2 is released, Ember 2 gets traction, & React v0.14 changes how context works, this post may be irrelevant.

But, when it comes to building something today, this has been my experience.

Show your support

Clapping shows how much you appreciated Eric Clemmons’s story.