The project team wants to move away from AngularJS (v1) for all new modules. What do we choose instead?
Angular 2+ (now at version 6) is the obvious first option.
This article is an attempt to explain why I prefer React instead.
Busy readers may want to jump straight to the summary at the end.
React is based on solid design principles, most of which I value.
Here I summarize the ones I find most relevant in our evaluation and comparison with Angular.
It’s easy to reuse components in Angular. However, it’s hard to compose them.
This proved to be a weakness of Angular in our experience. I think this is mainly due to components not being first-class, templates not being data, pervasive mutability (2-way bindings), and broad features you are forced to buy into (DI, custom namespaces).
Two concrete examples from our last Angular project:
- We built a non-trivial solution to deal with field-level permissions declaratively. We run into more than a few problems mostly fighting against Angular internals, other directives, race conditions, performace, and breakages after version changes. I haven’t yet proved it, but I’m pretty confident it can be done in React with just function compositions.
- Lack of “higher order components”. That is: passing components as arguments and making components/directives modify other components. Angular’s transclude feature is a limited and complex kludge compared to, again, simple function composition.
In contrast, React components are simple to compose: they are “first-class” and can be passed around (aka Higher-Order Components).
Here are some public extreme examples of what’s possible on React thanks its more composable nature:
- Relay: A declarative way of doing data-fetching and state management (using GraphQL).
- ClojureScript: A functional language which compiles to JS. It leverages and improves upon React. Here’s a great blog post described by one of React’s maintainers as “a turning point which got many people to take React seriously” (Dan Abramov’s My React List).
- ReasonML: A typed functional language which also leverages React naturally.
- ReduxJS leverages this to integrate with React. Idem for MobX-react
- Recompose library
Beyond the DOM
Most of React is independent of HTML and web browsers.
The core of React’s implementation works for other targets. Some examples:
- React Native: iOS, Android, Windows apps
- Server-side (non-SPA) applications: https://nextjs.org/
- Static rendering: Gatsbyjs
- Email templates: MJML
- Text-mode UIs: React Blessed
React’s architecture and concepts having been proven and applied successfully even outside of the JS world, like ComponentKit for iOS.
Stability and Dogfooding
The React team has been very cautious about breaking compatibility. In those few cases they did, they did so incrementally, with warnings in the library and tools to automate migration.
Did you know React 16 is a complete rewrite if its internals (codename: React Fiber) ? Fully backwards compatible.
More stable means less risk. It’s also more mature and has more years of refinement than Angular 2+.
Also: React is being used for many different public-facing applications: Facebook and Instagram (where React originated), Twitter, Airbnb, Netflix, Dropbox, WhatsApp.
Developer Experience / Debugging / Optimized for Tooling
Given its functional approach (that is, components being conceptually just functions of props and state), it’s quite easy to debug components.
React DevTools gives you an easy way to inspect the state of components in Chrome.
When using good state-management in your application (like Redux) you can achieve “live coding” which allows you to change the code of the app on the fly, while the app is running, without the need to reload the page. This is a big productivity boost.
React: “In general we resist adding features”. Angular, on the other hand, comes with a truckload of features which might not be the best fit for your solution.
Thanks to the other principles, it’s easy to integrate React with non-react libs and components.
For example, it’s easy to include a React component within an Angular app. We might follow this approach at our current project.
Also, it’s simpler to have multiple React apps coexist. Example, using Redux: https://redux.js.org/recipes/isolating-subapps.
React is based on Functional principles and meant to build UIs in a declarative way.
To be honest, tasks which are inherently about mutable state (like hanlding Forms, focus, or text selections) become harder under this approach.
React gives you options (like refs) for these special cases and there are already elegant common patterns to deal with them.
Some points I do not like about Angular
- NG Modules and its Dependency Injection
- Reinvents conditionals (IFs), Repeats (for), Switches, variable “scopes”, evaluation context.
- Custom “expression language”, with its own evaluation rules
- Additional non-HTML-compatible syntax
- “Transclusion”: a complex workaround to “pass” components as arguments to other components. This is trivial and much more flexible in React: you simply pass a function as parameter.
Non-functional, two-way binding approach
- More familiar (easier), but results in more complexity
- Makes it harder to compose and reuse
- Harder to unit test
- … to debug
- … to achieve live coding. That is, changing the JS code live, without reloading the browser page
Bigger. More complex. More lock-in.
What about WebComponents? Doesn’t it make React obsolete?
Web Components is an interesting standard. I have no experience using it yet. However, it looks like it doesn’t replace what React+Redux brings to the table: state management and keeping your DOM in sync with it.
While you might want to use web components for some pieces of UI, you’d still need to glue them together and manage the state of the application. And if a web component is complex enough, you might want to use React to implement it.
So, the way I see it, it makes a lot of sense to use React (or a similar library) both “around” web components, and inside of them.
TL;DR summary for busy readers
Pros of React:
- Grounded on solid principles
- Functional approach (vs two-way binding and shared state)
- less concepts to learn
- less concepts to fight against, less constraints
- less specialized tooling required
- simpler to unit test
- plays better with other libs
- Smaller. React’s public API for users is roughly 10 functions
- More stable, longer-term compatibility, more mature, less risk
- Bigger ecosystem (not just due to “popularity”. I think it’s also because its architecture allows for more leverage and composability)
- More innovation happening around it: functional, GraphQL, immutable collections, explicit state-management, ClojureScript, ReasonML, hot reloading, react native, server-side rendering, static site generation
- Better development tools
- More “composable” components
- Developers can leverage React’s knowledge for other rendering targets
- Natural hot code reloading (see Dan Abramov’s talk from 2015!)
- Simpler unit-testing
- More modular: pick your libs for each problem
- For me at least: it’s easier to understand and predict what React does vs Angular. Less “WTF” moments
Pros of Angular:
- It is more familiar to most developers. React’s functional and more “pure” approach (especially when using Redux) requires a different way of thinking
- Some things are easier to do in Angular (say, mutating any DOM element at any time). React requires more thinking for these kind of things, and sometimes more code
- In AngularJS, more decisions have been made for you
I have no bias to pick one or the other. There’s no hidden agenda. My only interest is on building better and more maintainable solutions.
I think there are enough long-term advantages to pick React for our project. I also believe it benefits the team and our customer, giving developers experience with technologies we can leverage for other rendering targets.