Repository: mui/material-ui (TypeScript)
Date: 2019-01-31 19:23:43+00:00
Task type: Bug Fix

Issue:
Select inside InputAdornment causes crash with material-ui/core 3.9.1 (works with 3.9.0)
<!--- Provide a general summary of the issue in the Title above -->

We use a Select as InputAdornment to let users choose a unit. It worked fine until material-ui/core 3.9.0, but now crashes the whole application.

<!--
    Thank you very much for contributing to Material-UI by creating an issue! ❤️
    To avoid duplicate issues we ask you to check off the following list.
-->

<!-- 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 🤔
<!---
    Describe what should happen.
-->
![material_ui_text_field_select_adornment](https://user-images.githubusercontent.com/10057387/52056798-80899100-2563-11e9-98c2-3af3392baba9.png)


## Current Behavior 😯
<!---
    Describe what happens instead of the expected behavior.
-->

```
Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    at invariant (VM77 0.chunk.js:110042)
    at scheduleWork (VM77 0.chunk.js:129702)
    at Object.enqueueSetState (VM77 0.chunk.js:122921)
    at FormControl.push../node_modules/react/cjs/react.development.js.Component.setState (VM77 0.chunk.js:139977)
    at Object.FormControl._this.handleClean [as onEmpty] (VM77 0.chunk.js:9830)
    at InputBase.checkDirty (VM77 0.chunk.js:14100)
    at InputBase.componentDidUpdate (VM77 0.chunk.js:14078)
    at commitLifeCycles (VM77 0.chunk.js:126998)
    at commitAllLifeCycles (VM77 0.chunk.js:128497)
    at HTMLUnknownElement.callCallback (VM77 0.chunk.js:110132)
    at Object.invokeGuardedCallbackDev (VM77 0.chunk.js:110181)
    at invokeGuardedCallback (VM77 0.chunk.js:110235)
    at commitRoot (VM77 0.chunk.js:128702)
    at completeRoot (VM77 0.chunk.js:130232)
    at performWorkOnRoot (VM77 0.chunk.js:130155)
    at performWork (VM77 0.chunk.js:130060)
    at performSyncWork (VM77 0.chunk.js:130034)
    at interactiveUpdates$1 (VM77 0.chunk.js:130322)
    at interactiveUpdates (VM77 0.chunk.js:112252)
    at dispatchInteractiveEvent (VM77 0.chunk.js:115068)
VM77 0.chunk.js:133526 

The above error occurred in the <InputBase> component:
    in InputBase (created by Context.Consumer)
    in WithFormControlContext(InputBase) (created by WithStyles(WithFormControlContext(InputBase)))
    in WithStyles(WithFormControlContext(InputBase)) (created by Input)
    in Input (created by WithStyles(Input))
    in WithStyles(Input) (created by TextField)
    in div (created by FormControl)
    in FormControl (created by WithStyles(FormControl))
    in WithStyles(FormControl) (created by TextField)
    in TextField (at ObjectField.js:202)
    in ObjectField (created by WithStyles(ObjectField))
    in WithStyles(ObjectField) (at ObjectFieldSet.js:38)
    in div (at ObjectFieldSet.js:28)
    in div (at ObjectFieldSet.js:27)
    in ObjectFieldSet (created by WithStyles(ObjectFieldSet))
    in WithStyles(ObjectFieldSet) (at ObjectEditPanel.js:91)
    in div (at ObjectEditPanel.js:90)
    in div (created by Typography)
    in Typography (created by WithStyles(Typography))
    in WithStyles(Typography) (at ObjectEditPanel.js:74)
    in div (created by Paper)
    in Paper (created by WithStyles(Paper))
    in WithStyles(Paper) (at ObjectEditPanel.js:66)
    in div (at ObjectEditPanel.js:65)
    in ObjectEditPanel (created by WithStyles(ObjectEditPanel))
    in WithStyles(ObjectEditPanel) (created by Connect(WithStyles(ObjectEditPanel)))
    in Connect(WithStyles(ObjectEditPanel)) (at ObjectEditPage.js:32)
    in div (at ObjectEditPage.js:30)
    in ObjectEditPage (created by Connect(ObjectEditPage))
    in Connect(ObjectEditPage) (at MainView.js:61)
    in div (at MainView.js:44)
    in div (at MainView.js:40)
    in div (at MainView.js:38)
    in MainView (created by Connect(MainView))
    in Connect(MainView) (at App.js:180)
    in div (at App.js:179)
    in div (at App.js:178)
    in Provider (at App.js:177)
    in MuiPickersUtilsProvider (at App.js:176)
    in MuiThemeProviderOld (at App.js:175)
    in App (at src/index.js:16)

Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://fb.me/react-error-boundaries to learn more about error boundaries.
VM77 0.chunk.js:110042 Uncaught Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
    at invariant (VM77 0.chunk.js:110042)
    at scheduleWork (VM77 0.chunk.js:129702)
    at Object.enqueueSetState (VM77 0.chunk.js:122921)
    at FormControl.push../node_modules/react/cjs/react.development.js.Component.setState (VM77 0.chunk.js:139977)
    at Object.FormControl._this.handleClean [as onEmpty] (VM77 0.chunk.js:9830)
    at InputBase.checkDirty (VM77 0.chunk.js:14100)
    at InputBase.componentDidUpdate (VM77 0.chunk.js:14078)
    at commitLifeCycles (VM77 0.chunk.js:126998)
    at commitAllLifeCycles (VM77 0.chunk.js:128497)
    at HTMLUnknownElement.callCallback (VM77 0.chunk.js:110132)
    at Object.invokeGuardedCallbackDev (VM77 0.chunk.js:110181)
    at invokeGuardedCallback (VM77 0.chunk.js:110235)
    at commitRoot (VM77 0.chunk.js:128702)
    at completeRoot (VM77 0.chunk.js:130232)
    at performWorkOnRoot (VM77 0.chunk.js:130155)
    at performWork (VM77 0.chunk.js:130060)
    at performSyncWork (VM77 0.chunk.js:130034)
    at interactiveUpdates$1 (VM77 0.chunk.js:130322)
    at interactiveUpdates (VM77 0.chunk.js:112252)
    at dispatchInteractiveEvent (VM77 0.chunk.js:115068)
```


## Steps to Reproduce 🕹
<!---
    Provide a link to a live example (you can use codesandbox.io) and an unambiguous set of steps to reproduce this bug.
    Include code to reproduce, if relevant (which it most likely is).

    This codesandbox.io template _may_ be a good starting point:
    https://codesandbox.io/s/github/mui-org/material-ui/tree/master/examples/create-react-app

    If you're using typescript a better starting point would be
    https://codesandbox.io/s/github/mui-org/material-ui/tree/master/examples/create-react-app-with-typescript

    If YOU DO NOT take time to provide a codesandbox.io reproduction, should the COMMUNITY take time to help you?

-->
Unfortunately, we cannot reproduce this Exception using codesandbox.

This is basically the code for our TextField.
The crash occurs only if a value is given for the Select Element.

```
render() {
    const { classes } = this.props;

    const inputProps = {};
    inputProps.endAdornment = this.createEndAdornment();

    return (
      <MuiThemeProvider theme={theme}>
        <MuiPickersUtilsProvider utils={DateFnsUtils}>
          <Provider store={store}>
            <div className={classes.root}>
              <TextField value="123" InputProps={inputProps} />
            </div>
          </Provider>
        </MuiPickersUtilsProvider>
      </MuiThemeProvider>
    );
  }

  createEndAdornment = () => {
    return (
    <InputAdornment position="end">
      <Select value="kg">
        <MenuItem key="kg" value="kg">
          kg
        </MenuItem>
        <MenuItem key="gram" value="gram">
          gram
        </MenuItem>
      </Select>
    </InputAdornment>
    );
  };
```

## 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  | v3.9.1  |
| React        | v16.7.0       |
| Browser      | Chrome and Firefox       |

### package.json
```
"dependencies": {
    "@date-io/date-fns": "^1.0.2",
    "@material-ui/core": "^3.9.0",
    "@material-ui/icons": "^3.0.2",
    "ajv": "^6.7.0",
    "autosuggest-highlight": "^3.1.1",
    "core-js": "^2.6.3",
    "date-fns": "^2.0.0-alpha.27",
    "fetch-mock": "^7.3.0",
    "file-saver": "^2.0.0",
    "lodash": "^4.17.11",
    "material-ui-pickers": "^2.1.2",
    "prop-types": "^15.6.2",
    "react": "^16.7.0",
    "react-autosuggest": "^9.4.3",
    "react-dom": "^16.7.0",
    "react-dropzone": "^6.2.4",
    "react-markdown": "^4.0.6",
    "react-redux": "^5.1.1",
    "react-scripts": "^.1.3",
    "redux": "^4.0.1",
    "redux-thunk": "^2.3.0",
    "socket.io-client": "^2.2.0"
  }
```


Issue discussion:
@joshwooding People are full of imagination 🔮. The prediction comes true. Now it's great. We have a reproduction.

@mtidei Thank you for the report. The fix is simple. We just need to make
https://github.com/mui-org/material-ui/blob/4422ce889293268cf7aaa8b1f5e63d465501a05f/packages/material-ui/src/InputBase/InputBase.js#L407
wrap all the children, the adornment included.
Is it clear enough? Do you want to submit a pull-request? :)

Solution patch:
diff --git a/packages/material-ui/src/InputAdornment/InputAdornment.js b/packages/material-ui/src/InputAdornment/InputAdornment.js
--- a/packages/material-ui/src/InputAdornment/InputAdornment.js
+++ b/packages/material-ui/src/InputAdornment/InputAdornment.js
@@ -6,6 +6,7 @@ import warning from 'warning';
 import Typography from '../Typography';
 import withStyles from '../styles/withStyles';
 import withFormControlContext from '../FormControl/withFormControlContext';
+import FormControlContext from '../FormControl/FormControlContext';
 
 export const styles = {
   /* Styles applied to the root element. */
@@ -64,25 +65,27 @@ function InputAdornment(props) {
   }
 
   return (
-    <Component
-      className={classNames(
-        classes.root,
-        {
-          [classes.filled]: variant === 'filled',
-          [classes.positionStart]: position === 'start',
-          [classes.positionEnd]: position === 'end',
-          [classes.disablePointerEvents]: disablePointerEvents,
-        },
-        className,
-      )}
-      {...other}
-    >
-      {typeof children === 'string' && !disableTypography ? (
-        <Typography color="textSecondary">{children}</Typography>
-      ) : (
-        children
-      )}
-    </Component>
+    <FormControlContext.Provider value={null}>
+      <Component
+        className={classNames(
+          classes.root,
+          {
+            [classes.filled]: variant === 'filled',
+            [classes.positionStart]: position === 'start',
+            [classes.positionEnd]: position === 'end',
+            [classes.disablePointerEvents]: disablePointerEvents,
+          },
+          className,
+        )}
+        {...other}
+      >
+        {typeof children === 'string' && !disableTypography ? (
+          <Typography color="textSecondary">{children}</Typography>
+        ) : (
+          children
+        )}
+      </Component>
+    </FormControlContext.Provider>
   );
 }
 


Test patch:
diff --git a/packages/material-ui/src/InputBase/InputBase.test.js b/packages/material-ui/src/InputBase/InputBase.test.js
--- a/packages/material-ui/src/InputBase/InputBase.test.js
+++ b/packages/material-ui/src/InputBase/InputBase.test.js
@@ -12,6 +12,8 @@ import FormControlContext from '../FormControl/FormControlContext';
 import InputAdornment from '../InputAdornment';
 import Textarea from './Textarea';
 import InputBase from './InputBase';
+import TextField from '../TextField';
+import Select from '../Select';
 
 describe('<InputBase />', () => {
   let classes;
@@ -492,6 +494,22 @@ describe('<InputBase />', () => {
         InputAdornment,
       );
     });
+
+    it('should allow a Select as an adornment', () => {
+      mount(
+        <TextField
+          value=""
+          name="text"
+          InputProps={{
+            endAdornment: (
+              <InputAdornment position="end">
+                <Select value="a" name="suffix" />
+              </InputAdornment>
+            ),
+          }}
+        />,
+      );
+    });
   });
 
   describe('prop: inputRef', () => {