Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.memo performance is worse than with React.PureComponent

Tags:

reactjs

Some time ago I did refactoring of cell renderers components to achieve performance gain (I have a huge table). I did refactoring from functional stateless components to PureComponent. E.g.:

import React from 'react';
import PropTypes from 'prop-types';

class SomeCell extends React.PureComponent {
  render() {
    const { pizzaOrder } = this.props;
    return (
      <>
        {pizzaOrder.name}
        <br />
        {pizzaOrder.price}
      </>
    );
  }
}

SomeCell .propTypes = {
  pizzaOrder: PropTypes.object,
};

export default SomeCell ;

Now I saw that React.memo was released so I updated to [email protected] and [email protected] (from 16.5.2) and refactored from PureComponent to React.memo with an expectation that it would be even faster (no lifecycle methods called, function smaller than class in memory etc...):

import React from 'react';
import PropTypes from 'prop-types';

const SomeCell = React.memo(function({ pizzaOrder }) {
  return (
    <>
        {pizzaOrder.name}
        <br />
        {pizzaOrder.price}
    </>
  );
});

SomeCell .propTypes = {
  pizzaOrder: PropTypes.object,
};

export default SomeCell;

And to my surprise, performance went significantly down.

Do you have any idea what could be the issue with it?

Profile data in prod mode (no addons in chrome) show that there's much more scripting happening then before with PureComponent (scripting time for my case went from 0.5s to 3.8sek).

EDIT: after some investigation, it seems that it is not an issue with React.memo but with a new version of React. I've reverted cell renderers to PureComponent and left new [email protected] version and the result (significantly slower performance) is still present

like image 387
dragonfly Avatar asked Oct 26 '18 13:10

dragonfly


2 Answers

As suggested by @skyboyer an issue was created in React Repository.

Summary of the issue (2018-11-11):

  • The issue was not related to React itself but to an uglify-es (buggy) optimization.
  • uglify-es is inlining code (that should not be inlined).
  • uglify-es is not actively maintained anymore.
  • The proposed solution is to use terser as a replacement.
  • If you are using uglifyjs-webpack-plugin or Webpack 4.x.x (that uses uglifyjs-webpack-plugin by default), you should change the minifier option in webpack configuration like this:

    const TerserWebpackPlugin = require('terser-webpack-plugin');
    
    module.exports = {
      //...
      optimization: {
        minimizer: [
          new TerserWebpackPlugin({ /* your config */ })
        ]
      }
    };
    
like image 74
rareyesdev Avatar answered Sep 22 '22 12:09

rareyesdev


TL;DR:

upgrade webpack to version 4.26

as they switched to terser as the default minimizer.

Background:

  • uglifyjs-webpack-plugin < v1.0 used the minifier uglify-js
  • however uglify-js does not support ES6, which resulted in a fork called uglify-es that was developed in the uglify-js repository, but under the harmony branch
  • uglifyjs-webpack-plugin v1.x switched to uglify-es for ES6 support
  • however uglify-es stopped being maintained: mishoo/UglifyJS2#3156 (comment)
  • which led to a fork called terser that has incorporated all of the unmerged PRs and will be where all new development occurs: https://github.com/fabiosantoscode/terser
  • terser-webpack-plugin was created, which is the terser equivalent of uglifyjs-webpack-plugin: https://github.com/webpack-contrib/terser-webpack-plugin
  • uglifyjs-webpack-plugin v2.x will be switching back to uglify-js, so any project that needs to support ES6 now needs to switch to terser-webpack-plugin.

Reference: webpack/commit

like image 39
Idan Dagan Avatar answered Sep 19 '22 12:09

Idan Dagan