Repository: mui/material-ui (TypeScript)
Date: 2019-06-19 20:07:14+00:00
Task type: Bug Fix

Issue:
[Textarea] Does not respond to rowsMax prop changes
As the title suggests, changing the rowsMax prop of a mounted TextField does nothing: the height of the textarea remains unchanged.

<!-- Checked checkbox should look like this: [x] -->
- [x] This is not a v0.x issue. <!-- (v0.x is no longer maintained) -->
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior 🤔
When I change the rowsMax prop of a mounted multiline TextField, I expect the height of the textarea to change to reflect the new rowsMax prop value. 

## Current Behavior 😯
When I change the rowsMax prop of a mounted multiline TextField, the height does not recalculate. If I then change the value, it still does not recalculate. If I add/remove a newline to the value, _then_ it will recalculate and display the proper number of rows.

_Bonus: Firefox only: if you update the value quickly enough, you will get an Invariant Violation - Maximum update depth exceeded (see [gist](https://gist.github.com/tasinet/fb3000a74b65a91ab36896b5f281c47e)). A way to reproduce this is to copy "asd asd asd " and paste repeatedly._

## Steps to Reproduce 🕹
Link: https://codesandbox.io/s/create-react-app-hqkug

1. Decrease rowsMax to 1 with the -- button
2. Observe that height is unchanged
3. Increase rowsMax to 3 with the ++ button
4. Observe that height is unchanged
5. Change a letter, e.g. Lorem -> Morem
6. Observe that height is unchanged
7. Insert a line break in the text area
8. Now the height recalculates to the rowsMax prop value

Bonus: Firefox only:

9. Set rowsMax to 4
10. Copy "asd asd asd " to your clipboard
11. Hold down ctrl+v inside the textarea
12. [Invariant Violation](https://gist.github.com/tasinet/fb3000a74b65a91ab36896b5f281c47e)

## Your Environment 🌎
<!---
    Include as many relevant details about the environment with which you experienced the bug.
    If you encounter issues with typescript please include version and tsconfig.
-->

| Tech         | Version |
|--------------|---------|
| Material-UI  | v4.1.1  |
| React        | v16.9.0        |
| Browsers      |  FF  67.0.1 (64-bit, Ubuntu) |
|       | Chrome 75.0.3770.80 (64-bit, Ubuntu) |

[Textarea] Does not respond to rowsMax prop changes
As the title suggests, changing the rowsMax prop of a mounted TextField does nothing: the height of the textarea remains unchanged.

<!-- Checked checkbox should look like this: [x] -->
- [x] This is not a v0.x issue. <!-- (v0.x is no longer maintained) -->
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior 🤔
When I change the rowsMax prop of a mounted multiline TextField, I expect the height of the textarea to change to reflect the new rowsMax prop value. 

## Current Behavior 😯
When I change the rowsMax prop of a mounted multiline TextField, the height does not recalculate. If I then change the value, it still does not recalculate. If I add/remove a newline to the value, _then_ it will recalculate and display the proper number of rows.

_Bonus: Firefox only: if you update the value quickly enough, you will get an Invariant Violation - Maximum update depth exceeded (see [gist](https://gist.github.com/tasinet/fb3000a74b65a91ab36896b5f281c47e)). A way to reproduce this is to copy "asd asd asd " and paste repeatedly._

## Steps to Reproduce 🕹
Link: https://codesandbox.io/s/create-react-app-hqkug

1. Decrease rowsMax to 1 with the -- button
2. Observe that height is unchanged
3. Increase rowsMax to 3 with the ++ button
4. Observe that height is unchanged
5. Change a letter, e.g. Lorem -> Morem
6. Observe that height is unchanged
7. Insert a line break in the text area
8. Now the height recalculates to the rowsMax prop value

Bonus: Firefox only:

9. Set rowsMax to 4
10. Copy "asd asd asd " to your clipboard
11. Hold down ctrl+v inside the textarea
12. [Invariant Violation](https://gist.github.com/tasinet/fb3000a74b65a91ab36896b5f281c47e)

## Your Environment 🌎
<!---
    Include as many relevant details about the environment with which you experienced the bug.
    If you encounter issues with typescript please include version and tsconfig.
-->

| Tech         | Version |
|--------------|---------|
| Material-UI  | v4.1.1  |
| React        | v16.9.0        |
| Browsers      |  FF  67.0.1 (64-bit, Ubuntu) |
|       | Chrome 75.0.3770.80 (64-bit, Ubuntu) |



Issue discussion:
Nice find. Looks like we need to change the threshold check to include the outer height e.g.

```diff
diff --git a/packages/material-ui/src/InputBase/Textarea.js b/packages/material-ui/src/InputBase/Textarea.js
index 519648805..1d896dab5 100644
--- a/packages/material-ui/src/InputBase/Textarea.js
+++ b/packages/material-ui/src/InputBase/Textarea.js
@@ -76,7 +76,9 @@ const Textarea = React.forwardRef(function Textarea(props, ref) {
     setState(prevState => {
       // Need a large enough different to update the height.
       // This prevents infinite rendering loop.
-      if (innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1) {
+      const overInnerThreshold = innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1;
+      const overOuterThreshold = outerHeight > 0 && Math.abs((prevState.outerHeight || 0) - outerHeight) > 1
+      if (overInnerThreshold || overOuterThreshold) {
         return {
           innerHeight,
           outerHeight,

```
Do you want to give it a go? :)

cc @oliviertassinari 
@joshwooding Would it be enough with? 
```diff
     setState(prevState => {
       // Need a large enough different to update the height.
       // This prevents infinite rendering loop.
-      if (innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1) {
+      if (outerHeight > 0 && Math.abs((prevState.outerHeight || 0) - outerHeight) > 1) {
         return {
           innerHeight,
           outerHeight,
```

@tasinet Feel free to submit a pull request :)
@oliviertassinari Probably, Thinking about it innerHeight is linked to the actual typed text and if it changes the outerHeight changes either way.
Nice find. Looks like we need to change the threshold check to include the outer height e.g.

```diff
diff --git a/packages/material-ui/src/InputBase/Textarea.js b/packages/material-ui/src/InputBase/Textarea.js
index 519648805..1d896dab5 100644
--- a/packages/material-ui/src/InputBase/Textarea.js
+++ b/packages/material-ui/src/InputBase/Textarea.js
@@ -76,7 +76,9 @@ const Textarea = React.forwardRef(function Textarea(props, ref) {
     setState(prevState => {
       // Need a large enough different to update the height.
       // This prevents infinite rendering loop.
-      if (innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1) {
+      const overInnerThreshold = innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1;
+      const overOuterThreshold = outerHeight > 0 && Math.abs((prevState.outerHeight || 0) - outerHeight) > 1
+      if (overInnerThreshold || overOuterThreshold) {
         return {
           innerHeight,
           outerHeight,

```
Do you want to give it a go? :)

cc @oliviertassinari 
@joshwooding Would it be enough with? 
```diff
     setState(prevState => {
       // Need a large enough different to update the height.
       // This prevents infinite rendering loop.
-      if (innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1) {
+      if (outerHeight > 0 && Math.abs((prevState.outerHeight || 0) - outerHeight) > 1) {
         return {
           innerHeight,
           outerHeight,
```

@tasinet Feel free to submit a pull request :)
@oliviertassinari Probably, Thinking about it innerHeight is linked to the actual typed text and if it changes the outerHeight changes either way.

Solution patch:
diff --git a/packages/material-ui/src/InputBase/Textarea.js b/packages/material-ui/src/InputBase/Textarea.js
--- a/packages/material-ui/src/InputBase/Textarea.js
+++ b/packages/material-ui/src/InputBase/Textarea.js
@@ -76,7 +76,7 @@ const Textarea = React.forwardRef(function Textarea(props, ref) {
     setState(prevState => {
       // Need a large enough different to update the height.
       // This prevents infinite rendering loop.
-      if (innerHeight > 0 && Math.abs((prevState.innerHeight || 0) - innerHeight) > 1) {
+      if (outerHeight > 0 && Math.abs((prevState.outerHeight || 0) - outerHeight) > 1) {
         return {
           innerHeight,
           outerHeight,


Test patch:
diff --git a/packages/material-ui/src/InputBase/Textarea.test.js b/packages/material-ui/src/InputBase/Textarea.test.js
--- a/packages/material-ui/src/InputBase/Textarea.test.js
+++ b/packages/material-ui/src/InputBase/Textarea.test.js
@@ -177,5 +177,23 @@ describe('<Textarea />', () => {
       wrapper.update();
       assert.strictEqual(getHeight(wrapper), lineHeight * rowsMax);
     });
+
+    it('should update its height when the "rowsMax" prop changes', () => {
+      const lineHeight = 15;
+      const wrapper = mount(<Textarea rowsMax={3} />);
+      setLayout(wrapper, {
+        getComputedStyle: {
+          'box-sizing': 'content-box',
+        },
+        scrollHeight: 100,
+        lineHeight,
+      });
+      wrapper.setProps();
+      wrapper.update();
+      assert.strictEqual(getHeight(wrapper), lineHeight * 3);
+      wrapper.setProps({ rowsMax: 2 });
+      wrapper.update();
+      assert.strictEqual(getHeight(wrapper), lineHeight * 2);
+    });
   });
 });