Facebook's React vs AngularJS: A Closer Look
When we launched React | A JavaScript library for building user interfaces two weeks ago there were a few comparisons to AngularJS (Facebook’s New React JavaScript Library Tutorial Rewritten in AngularJS). We already talked about how React works and its philosophy (React | Why did we build React?), but let's get a little more concrete and look at React and Angular code side-by-side.
To set the record straight: React components are far more powerful than Angular templates; they should be compared with Angular's directives instead. So I took the first Google hit for "AngularJS directive tutorial" (AngularJS Directives Tutorial - Fundoo Solutions), rewrote it in React and compared them.
All of the code in this post is on GitHub at https://github.com/petehunt/angu....
The app
The tutorial creates an "n star" rating widget. Here's what the widget looks like:
You can play with it here: http://jsfiddle.net/abhiroop/G3U...
The HTML page
First let's look at Angular's HTML:
<!DOCTYPE html>
<html ng-app="FundooDirectiveTutorial">
<head>
<title>Rating Directive Demo</title>
<link rel="stylesheet" href="rating.css"/>
</head>
<body ng-controller="FundooCtrl">
Rating is {{rating}} <br/>
Clickable Rating <br/>
<div fundoo-rating rating-value="rating" max="10" on-rating-selected="saveRatingToServer(rating)"></div>
<br/>
Readonly rating <br/>
<div fundoo-rating rating-value="rating" max="10" readonly="true"></div>
<script type="text/javascript" src="Page on Googleapis"></script>
<script type="text/javascript" src="rating.js"></script>
</body>
</html>
It's a pretty straightforward document, except it includes magical HTML attributes. With React you simply mount a component into a plain HTML page via JavaScript. Here's what it looks like:
<!DOCTYPE html>
<html>
<head>
<title>Rating Directive Demo</title>
<link rel="stylesheet" href="rating.css"/>
<script type="text/javascript" src="http://dragon.ak.fbcdn.net/hphotos-ak-ash3/851560_459383004151757_22266_n.js"></script>
<script type="text/javascript" src="http://dragon.ak.fbcdn.net/hphotos-ak-prn1/851582_580035725361422_42012_n.js"></script>
<script type="text/jsx" src="rating.js"></script>
</head>
<body>
</body>
</html>
Nothing interesting here (except we're using an in-browser transformer for development; see React | Getting Started for more info). Some JavaScript will mount our component in document.body.
The CSS is shared between the Angular and React versions so I've omitted it.
The JavaScript
Let's look at Angular's JavaScript. It defines a module, a controller and a directive:
angular.module('FundooDirectiveTutorial', [])
.controller('FundooCtrl', function($scope, $window) {
$scope.rating = 5;
$scope.saveRatingToServer = function(rating) {
$window.alert('Rating selected - ' + rating);
};
})
.directive('fundooRating', function () {
return {
restrict: 'A',
template: '<ul class="rating">' +
'<li ng-repeat="star in stars" ng-class="star" ng-click="toggle($index)">' +
'\u2605' +
'</li>' +
'</ul>',
scope: {
ratingValue: '=',
max: '=',
readonly: '@',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
var updateStars = function() {
scope.stars = [];
for (var i = 0; i < scope.max; i++) {
scope.stars.push({filled: i < scope.ratingValue});
}
};
scope.toggle = function(index) {
if (scope.readonly && scope.readonly === 'true') {
return;
}
scope.ratingValue = index + 1;
scope.onRatingSelected({rating: index + 1});
};
scope.$watch('ratingValue', function(oldVal, newVal) {
if (newVal) {
updateStars();
}
});
}
}
});
Notice the HTML template defined in a string. The provided linking function tells Angular how to imperatively update the DOM when the rating changes via an explicit callback. There is also a scope which is similar to, but behaves differently than, a JavaScript environment.
Let's look at the React version.
/** @jsx React.DOM */
var FundooRating = React.createClass({
render: function() {
var items = [];
for (var i = 1; i <= this.props.max; i++) {
var clickHandler = this.props.onRatingSelected && this.props.onRatingSelected.bind(null, i);
items.push(<li class={i <= this.props.value && 'filled'} onClick={clickHandler}>{'\u2605'}</li>);
}
return <ul class="rating">{items}</ul>;
}
});
var FundooDirectiveTutorial = React.createClass({
getInitialState: function() {
return {rating: 5};
},
handleRatingSelected: React.autoBind(function(rating) {
this.setState({rating: rating});
alert('Rating selected - ' + rating);
}),
render: function() {
return (
<div>
Rating is {this.state.rating}<br/>
Clickable Rating <br/>
<FundooRating value={this.state.rating} max="10" onRatingSelected={this.handleRatingSelected} />
<br />
Readonly rating <br/>
<FundooRating value={this.state.rating} max="10" />
</div>
);
}
});
React.renderComponent(<FundooDirectiveTutorial />, document.body);
The markup syntax you see is JSX and we've discussed it at length in React | JSX Syntax. We prefer it, but it's not required to use React.
React doesn't have templates since we use JavaScript to generate the markup (via JSX or function calls).
There's no linking function because React figures out how to most efficiently update the DOM for you when your data changes. Just write your render() function and React will keep the UI up-to-date for you.
There are no scopes or other nonstandard concepts besides components (which are just objects) since you're just using plain, familiar JavaScript to express your display logic.
We have plenty of documentation starting with React | Tutorial to explain how all of this works. The important takeaway is that you provide a render() method that declaratively specifies how you want your UI to look. When the data changes, React calls your render() method again, diffs the old return value with the new one and figures out how to update the DOM for you.
React vs AngularJS by the (highly unscientific) numbers
Number of concepts to learn
- React: 2 (everything is a component, some components have state). As your app grows, there's nothing more to learn; just build more modular components.
- AngularJS: 6 (modules, controllers, directives, scopes, templates, linking functions). As your app grows, you'll need to learn more concepts.
Lines of code
- React: 47 (12 HTML, 35 JS)
- AngularJS: 64 (18 HTML, 46 JS)
Comparing technology is hard.
It's hard to compare two technologies in an objective way in a single blog post. I'm sure it's possible to code golf the Angular example to be smaller than React, so certainly don't take these numbers too seriously.
Using React and AngularJS together
We've designed React from the beginning to work well with other libraries. Angular is no exception. Let's take the original Angular example and use React to implement the fundoo-rating directive.
First, let's bring back the original AngularJS HTML page and add the React dependencies:
<!DOCTYPE html>
<html ng-app="FundooDirectiveTutorial">
<head>
<title>Rating Directive Demo</title>
<link rel="stylesheet" href="rating.css"/>
</head>
<body ng-controller="FundooCtrl">
Rating is {{rating}} <br/>
Clickable Rating <br/>
<div fundoo-rating rating-value="rating" max="10" on-rating-selected="saveRatingToServer(rating)"></div>
<br/>
Readonly rating <br/>
<div fundoo-rating rating-value="rating" max="10" readonly="true"></div>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.6/angular.min.js"></script>
<script type="text/javascript" src="http://dragon.ak.fbcdn.net/hphotos-ak-ash3/851560_459383004151757_22266_n.js"></script>
<script type="text/javascript" src="rating-react.build.js"></script>
<script type="text/javascript" src="rating-angular.js"></script>
</body>
</html>
Note: for this example we're precompiling a rating-react.js file using JSX syntax to rating-react.build.js using react-tools.
Next let's strip down the React version to the bare minimum we need to support the directive:
/** @jsx React.DOM */
window.FundooRating = React.createClass({
render: function() {
var items = [];
for (var i = 0; i < this.props.scope.max; i++) {
var clickHandler = this.props.scope.$apply.bind(this.props.scope, this.props.scope.toggle.bind(null, i));
items.push(<li class={i < this.props.scope.ratingValue && 'filled'} onClick={clickHandler}>{'\u2605'}</li>);
}
return <ul class="rating">{items}</ul>;
}
});
And finally, here's what remains of the Angular JavaScript code:
angular.module('FundooDirectiveTutorial', [])
.controller('FundooCtrl', function($scope, $window) {
$scope.rating = 5;
$scope.saveRatingToServer = function(rating) {
$window.alert('Rating selected - ' + rating);
};
})
.directive('fundooRating', function () {
return {
restrict: 'A',
scope: {
ratingValue: '=',
max: '=',
readonly: '@',
onRatingSelected: '&'
},
link: function (scope, elem, attrs) {
scope.toggle = function(index) {
if (scope.readonly && scope.readonly === 'true') {
return;
}
scope.ratingValue = index + 1;
scope.onRatingSelected({rating: index + 1});
};
scope.$watch('ratingValue', function(oldVal, newVal) {
React.renderComponent(window.FundooRating({scope: scope}), elem[0]);
});
}
}
});
We've changed the watch expression to simply call React.renderComponent() whenever the data changes. React is smart enough to do this efficiently. You don't have to write any code to update your UI.
This version clocks in at 62 loc, which is between the pure-React and pure-Angular version.
The conclusion
AngularJS is a great tool for building web apps.
If you like Angular, we think you'll love React because reactive updates are so easy and composable components are a simple and powerful abstraction for large and small applications.
Head on over to React | A JavaScript library for building user interfaces
write by Pete Hunt