React vs Angular: Slow rendering with React

I was doing a comparison of Angular and React and decided to try out a performance test to see how fast a large(ish) list would render in both frameworks.

When I got done with my React prototype with some basic currency formating, it was taking ~2 seconds to render on my fast laptop. With Angular it was barely noticeable -- only when I switched to my phone did it have a noticeable lag.

This was very surprising because I was told that React was supposed to beat the pants off of Angular for performance, but it appears that the opposite is true in this case.

I distilled my prototype down to a very simple app to try to isolate the issue: https://github.com/pselden/react-render-test

In this sample, it's taking almost 200ms to render this simple list after changing the language, and I'm barely doing anything.

Am I doing something wrong here?

/** @jsx React.DOM */
'use strict';

var React = require('react'),
    Numbers = require('./Numbers');

var numbers = []
for(var i = 0; i < 2000; ++i){
    numbers.push(i);
}

var App = React.createClass({
    getInitialState: function() {
        return { locale: 'en-US' };
    },

    _onChangeLocale: function(event) {
        this.setState({locale: event.target.value});
    },

    render: function() {
        var currentLocale = this.state.locale;

        return (
            <div>
                <select
                    onChange={this._onChangeLocale}>
                    <option value="en-US">English</option>
                    <option value="fr-FR">French</option>
                </select>
                <Numbers numbers={numbers} locales={[currentLocale]} />
            </div>
        );
    }
});

module.exports = App;
/** @jsx React.DOM */
'use strict';

var React = require('react'),
    ReactIntlMixin = require('react-intl');

var Numbers = React.createClass({
    mixins: [ReactIntlMixin],

    getInitialState: function() {
        return {
            numbers: this.props.numbers
        };
    },

    render: function() {
        var self = this;
        var list = this.state.numbers.map(function(number){
            return <li key={number}>{number} - {self.formatNumber(number, {style: 'currency', currency: 'USD'})}</li>
        });

        return <ul>{list}</ul>;
    }
});

module.exports = Numbers;

PS: Added an angular version: https://github.com/pselden/angular-render-test

Edit: I opened an issue with react-intl and we investigated and found that there was not that much overhead with using https://github.com/yahoo/react-intl/issues/27 -- it's simply React itself that is slower here.


Solution 1:

This is definitely an interesting test case.

If you take a look at the timelines, you can see that Angular is finished handling the change event in a mere 20ms. The remainder of the time is spent in layout and repaint.

Angular Timeline

React (using a production build, your repo uses dev by default) takes about 59ms. Again, the rest of the time is spent in layout and repaint.

React Timeline

If you take a look at the CPU flame charts, you can see that Angular appears to be doing a lot less work.

Angular:

Angular CPU Graph

React:

React CPU Graph

React provides a pretty good optimization hook in the form of shouldComponentUpdate that is especially useful when a single instance of a component out of thousands should update and the others should stay the same; it's a technique I use in this demo (try it out in an incognito window; I've found some Chrome extensions make layout/repaint times much higher—for me, adding/removing single elements once the list is 1000 long takes ~13ms, changing the size/color of an element takes ~1ms). However, it does no good when every element needs to update.

My guess is that Angular will be faster at changing most or all of the elements in your table, and React will be quite proficient at updating select entries when using shouldComponentUpdate.

Solution 2:

I'm surprised nobody mentioned PureRenderMixin. It implements shouldComponentUpdate so you don't have to worry about it.

Also, I wonder if React Performance Tools would turn up something useful?

And I'm surprised to hear Angular is faster than React after watching this talk from Ryan Florence.