Repository: mui/material-ui (TypeScript)
Date: 2018-05-08 10:48:09+00:00
Task type: Bug Fix

Issue:
Not needed Rerender
Hello, 

there is an tool called "why-did-you-rerender" (https://github.com/maicki/why-did-you-update).
I dropped it into my application to see which components rereder even it is not necessary. 
![image](https://user-images.githubusercontent.com/15649454/39744269-fde77e70-52a3-11e8-853c-464ae63398d9.png)

As you can see on the screenshot, the TouchRipple and the TransitionGroup always rerender. Is that right or a bug?





Issue discussion:
FYI: I think this happens, when I use a MenuItem in a Drawer and an AppBar
@janhoeck This happens everywhere, and it's mostly on purpose #10778. However, the TouchRipple is a good one. I think that we can safely use a pure logic.
Okay I just want to inform you and thanks for the information :) I will close this

Solution patch:
diff --git a/packages/material-ui/src/ButtonBase/TouchRipple.js b/packages/material-ui/src/ButtonBase/TouchRipple.js
--- a/packages/material-ui/src/ButtonBase/TouchRipple.js
+++ b/packages/material-ui/src/ButtonBase/TouchRipple.js
@@ -87,7 +87,7 @@ export const styles = theme => ({
   },
 });
 
-class TouchRipple extends React.Component {
+class TouchRipple extends React.PureComponent {
   state = {
     nextKey: 0,
     ripples: [],


Test patch:
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.test.js b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
--- a/packages/material-ui/src/ButtonBase/ButtonBase.test.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.test.js
@@ -4,6 +4,7 @@ import keycode from 'keycode';
 import { assert } from 'chai';
 import PropTypes from 'prop-types';
 import { spy, useFakeTimers } from 'sinon';
+import rerender from 'test/utils/rerender';
 import { createShallow, createMount, getClasses, unwrap } from '../test-utils';
 import TouchRipple from './TouchRipple';
 import ButtonBase from './ButtonBase';
@@ -658,4 +659,22 @@ describe('<ButtonBase />', () => {
       assert.strictEqual(wrapper.find('.focusVisible').length, 1);
     });
   });
+
+  describe('rerender', () => {
+    beforeEach(() => {
+      rerender.spy();
+    });
+
+    afterEach(() => {
+      rerender.reset();
+    });
+
+    it('should not rerender the TouchRipple', () => {
+      const wrapper = mount(<ButtonBase>foo</ButtonBase>);
+      wrapper.setProps({
+        children: 'bar',
+      });
+      assert.strictEqual(rerender.updates.length, 1);
+    });
+  });
 });
diff --git a/test/utils/rerender.js b/test/utils/rerender.js
new file mode 100644
--- /dev/null
+++ b/test/utils/rerender.js
@@ -0,0 +1,30 @@
+import React from 'react';
+import getDisplayName from 'recompose/getDisplayName';
+
+function createComponentDidUpdate(instance) {
+  return function componentDidUpdate() {
+    const displayName = getDisplayName(this);
+    instance.updates.push({
+      displayName,
+    });
+  };
+}
+
+// Inspired by https://github.com/maicki/why-did-you-update
+class Rerender {
+  updates = [];
+
+  spy = () => {
+    this.savedCDU = React.Component.prototype.componentDidUpdate;
+    React.Component.prototype.componentDidUpdate = createComponentDidUpdate(this);
+  };
+
+  savedCDU = null;
+
+  reset = () => {
+    this.updates = [];
+    React.Component.prototype.componentDidUpdate = this.savedCDU;
+  };
+}
+
+export default new Rerender();