If a textfield that had focus, is rerendered by setState
, it looses focus.
+1
I have to use the onBlur event with defaultValue to get the Textfields working smoothly.
anyone have a workaround to make this work?
I don't think this an issue any more. See code below:
//taken from examples/webpack-example
/** In this file, we create a React component which incorporates components provided by material-ui */
const React = require('react');
const RaisedButton = require('material-ui/lib/raised-button');
const TextField = require('material-ui/lib/text-field');
const Dialog = require('material-ui/lib/dialog');
const ThemeManager = require('material-ui/lib/styles/theme-manager');
const LightRawTheme = require('material-ui/lib/styles/raw-themes/light-raw-theme');
const Colors = require('material-ui/lib/styles/colors');
const Main = React.createClass({
childContextTypes: {
muiTheme: React.PropTypes.object,
},
getInitialState () {
return {
muiTheme: ThemeManager.getMuiTheme(LightRawTheme),
counter: 0,
};
},
getChildContext() {
return {
muiTheme: this.state.muiTheme,
};
},
componentWillMount() {
let newMuiTheme = ThemeManager.modifyRawThemePalette(this.state.muiTheme, {
accent1Color: Colors.deepOrange500,
});
this.setState({muiTheme: newMuiTheme});
},
componentDidMount() {
this.refs.textField.focus();
},
render() {
console.log(this.state.counter);
let containerStyle = {
textAlign: 'center',
paddingTop: '200px',
};
let standardActions = [
{ text: 'Okay' },
];
return (
<div style={containerStyle}>
<Dialog
title="Super Secret Password"
actions={standardActions}
ref="superSecretPasswordDialog">
1-2-3-4-5
</Dialog>
<h1>material-ui</h1>
<h2>example project</h2>
<TextField ref="textField" onChange = {this._incrementStateCounter}/>
<RaisedButton label="Super Secret Password" primary={true} onTouchTap={this._handleTouchTap} />
</div>
);
},
_handleTouchTap() {
this.refs.superSecretPasswordDialog.show();
},
_incrementStateCounter () {
this.setState({counter: this.state.counter + 1});
},
});
module.exports = Main;
Here's the app in action (with updated state being logged in the console as the TextField input changes). You can see that the TextField retains focus:
I still get this. @shaurya947 example didn't reproduced the bug since the TextField didn't have an initial value property.
to reproduce use
<TextField ref="textField" value={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>
or
<TextField ref="textField" defaultValue={this.state.valueToBeEdited} onChange = {this._incrementStateCounter}/>
I am also seeing what appears to be a regression of this issue. My TextFields are losing focus on re-render. I'm setting the value
prop of the TextField
, so it is a controlled component.
I do notice a new warning in the console: 'TextField is changing an uncontrolled input of type text to be controlled'. I don't recall seeing that before, not sure if it's relevant here or not. The warning appears once I enter the first character into the TextField
.
Through a little debugging, it appears that something is causing the TextField
to lose focus. The internal onBlur
handler is invoked, which toggles the isFocused
internal state of the TextField
to false. If I log document.activeElement
during the onBlur
handler (to determine what component has stole focus) then it logs the root of the document (body).
I was able to narrow this down further by determining that the source of the blur event was the first MenuItem
within a Menu
elsewhere in the app. Setting the disableAutoFocus
property to true
resolved my issue. Hope this helps someone else.
I've opened a separate issue to capture this: #4387.
I can confirm this is still an issue...
Seeing it in a very simple login form - I'm using this in conjunction with redux forms and when I interact with the form it causes a re-render (touched a field in redux-form parlance) which loses the focus.
As a workaround, I'm only re-rendering the actual form if there been a change in the error state.
You can reproduce this issue easily by putting the TextField
within a Table
set as selectable=true
. Whenever you click on a text field to start editing it, the line will get the focus, thus change its background color (I guess some props
of the line might be set as selected=true
), thus trigger a re-render, thus you'll lose the focus...
So you basically can't use a TextField
within a selectable
Table
, not sure it's a good UX practice anyway, but still :)
We noticed that on our project, I can try to push a reproducer.
Any work around to this? Also noticing this when using a regular input text area inside of a table.
@dalexander01 Found a "solution", but it seems very redux-form specific as was experiencing the same problem when not using md input components. If its of any use I can post a snippet!
@deyceg please do, it cant hurt 馃槃 . Just run into this issue with this combination of a <Dialog/>
and a <TextField/>
.
<Dialog
open={this.props.showDialog}
title={'New ' + this.props.type}
autoScrollBodyContent={true}
actions={actions}
bodyStyle={styles.dialogContent}
>
<SelectField
name='sub_type'
value={this.props.top_card.sub_type}
onChange={(e, k, payload) => this.props.topCardSelectChange('sub_type', payload)}
floatingLabelText='Type'
>
{menuItemsJSX}
</SelectField>
<TextField
name='title'
className='story-title'
value={this.props.top_card.title}
onChange={this.props.topCardChange}
floatingLabelText='Title'
fullWidth={true}
multiLine={true}
style={styles.title}
/>
<TextField />
</Dialog>
I solved it for my case, you need to provide an unique id to that text field.
The id should not change after re-render though. This way React can keep track of which element was focused or not.
P.S. I had textfields in an array which is rendered via myArray.map, there you have to provide same keys when rerender.
EDIT: I tested again, just having the "key" on myArray to be the same across rerenders solved the issue.
The textfield id changed to shortid.generate().
params.map(function(p,i){
return(
<div key={i}> <--- this will always be the same for this particular row.
<div className='param-inner'>
<TextField id={shortid.generate()} value = {p.value} onChange={this.updateParam.bind(this,i)}/> <-- id here can be random, but it is necessary to avoid browser warning "we don't have enough information..."
</div>
<div className='param-inner'>{p.unit}</div>
</div>
)
}.bind(this));
Anyone managed to find a workaround this issue (without using refs
)? It seems to be the combination of onChange
and setting the value
prop to a state key that makes the input loose focus.
I had the following issue too, I solved it though by having taking the SelectField implementation if you see it internally is calling a DropdownMenu https://github.com/callemall/material-ui/blob/ccf712c5733508784cd709c18c29059542d6aad1/src/SelectField/SelectField.js#L192
so I also need it to add a DropDownMenu this internally is wrapping the components in a Menu, I added disableAutoFocus to make my TextField keep the focus on it as show bellow.
Maybe we can expose a DropDownMenu component from the SelectField and allow autofocus to be disable with props.
One workaround for this for those who are struggling with it and just want something to work, though its possible its likely not applicable in some situations and could present issues (I am not entirely certain myself though) is to use a variable on the instance rather than the state. So instead of this.state.value
it would be this.value
that onChange
would be updating. The component would not have a value
prop. Then on the onChange
handler you would use e.target.value
.
The only difference I know of between a variable on the instance vs. the state is that the instance var will not trigger a re-render. Though there could be other reasons so I'm hoping other's will catch my slack on this one.
Still getting this error. Any work arounds?
The workaround I found was to override the shouldComponentUpdate function to my component and then return false when I changed the state values used by my textfields.
`shouldComponentUpdate(nextProps, nextState) {
if(nextState.email != this.state.email) {
return false;
}
else if(nextState.password != this.state.password) {
return false;
}
else return true;
}
`
Doing it this way fixed my issue. The issue seems to be the component rendering every time the state changes which then resets focus. So by using the above function to disable rendering when you do an onChange event to set state values the component doesn't reload.
@Aspintyl000 this works... somewhat. When I am setting the value of the textfield through the 'value' prop, and I am setting that prop to 'this.state.value', nothing shows up. Although, when I remove that, it seems to work.
i am using textfields in a table row, and this is a problem :( any progress?
I was also getting the same error . But after using componentDidMount() {
this.refs.textField.focus();
}
it does not lose focus. But it does not show the input value.
I tried to add <TextField />
in a <Table />
. Like the above situations, I cannot focus the input.
My solution: add a onClick
event to the selectable=false
. After your input operation, use an onBlue
event to set the table's selectable=true
.
Pretty obnoxious behavior.
I had experienced this after wrapping two input boxes with a styled-component. The outer div completely broke the behavior. This is a terrible flaw in the design of this library.
So this is a pretty terrible workaround but the following works, mostly:
````
onSomeValueChanged = event => {
this.props.someValueChanged(event.target.value);
const { target } = event;
setTimeout(() => {
target.focus();
}, 10);
};
````
Yeah, not pretty...
@scotmatson "Pretty obnoxious behavior...terrible flaw in the design of this library". This is free software, made by volunteers...good catch on the relation to styled-components
though.
@scotmatson I can confirm that, at least in my case, the issue is resolved if I turn the Container = styled.div
that I have as a wrapper into a normal, non-styled-component div
.
Edit:
And I also share your frustrations. It would be really nice if these two libraries worked together flawlessly. This issue and the specificity issue are two things that I hope get ironed out soon but I am grateful for the people volunteering their time.
Edit2:
I Just discovered what was causing the problem in my case: declaring styled-component components in the render method! This was causing the components to be recreated on every re-render and React couldn't track which element had focus across the re-render boundary. This was also leading to a few other re-render issues for me.
Moving all my styled-component declarations to outside of my component resolved the losing focus issues for me. Probably a noob mistake but no one has mentioned that here so I thought I would come back and report.
The problem I seem to be having is that the focus is lot whenever a TextFields (or Input) is being rendered inside a object.map(). The TextFields outside the maps are working perfectly. Am I missing any props or anything else? I am totally lost
I tried almost every solution suggested in this thread without any luck.
The rough structure is this
Main File
...
function Steps(props) {
const { steps } = props;
if(steps) {
return steps.map((step, index) => (
<div key={(step.order === '' ? index : step.order)}>
<NewUxCard step={step} index={index} onChange={props.onChange}
onChangeCard={props.onChangeCard} onParamChange={props.onParamChange}/>
</div>
));
}
}
<div>
<TextField/> //working
<Steps steps={this.props.ux.steps} onChange={this.onChange}
onChangeCard={this.handleChangeCard} onParamChange={this.handleParamChange} />
</div>
NewUxCard
...
<div>
<TextField type="text" fullWidth id={"qq"+this.props.index} label="Name"
value={this.props.step.name} onChange={this.props.onChangeCard('name', this.props.index)} margin="normal" /> //not working - loses focus on each key stroke
</div>
Worth noting that I am using Redux-React, so the state changes goes through input->action->reducer
Any ideas? Thanks
EDIT 1:
Further investigation of the problem, I have narrowed it down and solved by removing the react function Steps(props) that is used to create a Tag
So before, the structure was
@piltsen I just encountered this problem on v1.5.0 while rendering TextFields in a .map()
- I narrowed it down to my TextField key being the same value as the input, so when it changed it likely caused the TextField to mount again and lose focus. Not sure if that helps you - though I'm sure by now you have sorted it
This is also a symptom of using render props. Doing something like
<HigherOrderComponent
render={hocProps => <TextField value={...} onChange={...} />
/>
can cause this behaviour. To fix, pass a fragment instead if permitted by design:
<HigherOrderComponentThatDoesntPassOwnState
component={<TextField {...this.props}/>}
/>
In my case seems not focus was lost but cursor disappear from TextField. I can check that document.activeElement is equal to my TextField, I can use keyboard command and mouse wheel to change value (type number), but every type reducer run render happen and cursor lost.
Then in top of render I search by id my TextField and make
MyTextField.blur();
MyTextField.focus();
, which is not helping. But when I make focus() with some timeout, it seems to be working.
Edit:
Well, inside HOC it still not working, so it seems need to push it up anyway.
@younes0 I can confirm that fixes it for me too. In my case, I had a helper stateless component that would return a TextField with a few common props set and then spread the rest of the props onto it, so I didn't have to specify things like className
or variant
multiple times on the entire form. My text fields were losing focus as soon as any change was made to the form, and removing the helper function and just making raw TextField
s over and over again fixed it.
Can this be re-opened, or should a new issue be filed?
I'm still getting this issue, I'm using 3.2.0
@OmarDavilaP Can you create a new issue with a reproduction?
I solved it for my case, you need to provide an unique id to that text field.
...
The id should not change after re-render though. This way React can keep track of which element was focused or not.
P.S. I had textfields in an array which is rendered via myArray.map, there you have to provide same keys when rerender.EDIT: I tested again, just having the "key" on myArray to be the same across rerenders solved the issue.
The textfield id changed to shortid.generate().params.map(function(p,i){ return( <div key={i}> <--- this will always be the same for this particular row. <div className='param-inner'> <TextField id={shortid.generate()} value = {p.value} onChange={this.updateParam.bind(this,i)}/> <-- id here can be random, but it is necessary to avoid browser warning "we don't have enough information..." </div> <div className='param-inner'>{p.unit}</div> </div> ) }.bind(this));
Yup, it solves the issue in my case, basically I was using:
<NewTextField key={user.id+Math.random()}>
I replaced it by:
<NewTextField key={user.id}>
So, yes it seems a bad practice generate random Keys each time it re-render the component.
For me the issue was, I created a wrapper function for TextField, however i declared that function within the my component function, once moved outside my function everything worked as expected!
I created a sendbox sample https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
I am having this problem with conditional rendering. I have a a javascript object in the render that returns various components in the .js file. I'm not describing it correctly, sorry. When I fix it I'll edit this post, if I remember to.
edit: Yay, I fixed it! I believe the way I was conditionally rendering was rendering each component even when it wasn't displayed. I moved the logic outside of the render and it seems to work fine now. Here's the old code:
edit: i can't into styling on this thing so nevermind.
I encountered this issue today and banged my head on it for a good few hours. In my case, what I was doing wrong was wrapping the TextField
in a stateless component to better organise my page (don't render some if data is loading, etc etc), but I left the useState
callback that controlled the TextField
in the parent component.
For posterity, here's a small codesandbox link to reproduce my stupidity: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb
The question now is, how would I create my custom component wrapping TextField
? Both HOC and createElement
seems to incur in this bug:
const MyField= ({ name, ...props }) => (
<TextField
fullWidth
variant="outlined"
size="small"
name={name}
{...props}
/>
);
const MyField= ({ name, ...rest }) => {
return React.createElement(TextField, {
fullWidth: true,
variant: "outlined",
size: "small",
name: name,
{...rest}
});
};
Nevermind, the above approach works fine - I realized I was defined the custom component inside the parent component, which caused the issue. When moved outside of the parent scope (like it should be) it works fine - I updated the previous sandbox to show the correct approach: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb
For me the issue was, I created a wrapper function for TextField, however i declared that function within the my component function, once moved outside my function everything worked as expected!
I created a sendbox sample https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
This was my problem. Thank you!
Nevermind, the above approach works fine - I realized I was defined the custom component inside the parent component, which caused the issue. When moved outside of the parent scope (like it should be) it works fine - I updated the previous sandbox to show the correct approach: https://codesandbox.io/s/mui-textedit-losefocus-g6xxb
Thank you. This worked!
For me the issue was, I created a wrapper function for TextField, however i declared that function within the my component function, once moved outside my function everything worked as expected!
I created a sendbox sample https://codesandbox.io/s/material-demo-msbxl?fontsize=14&hidenavigation=1&theme=dark
@fleischman718 thanks for figuring this one out! Removing the wrapping component function for a TextField inside of a parent component's definition fixed my issue!
It took me a sec to figure out what was going on in the sandbox so I put a summary below:
import { TextField } from '@material-ui/core';
const ParentComponent = () => {
// DON'T DO THIS!!!
// fix issue by moving this component definition outside of the ParentComponent
// or assign the TextField to a variable instead
const TextFieldWrapper = () => (
<TextField />
);
return (
<div>
<TextFieldWrapper />
</div>
)
}
Most helpful comment
@scotmatson I can confirm that, at least in my case, the issue is resolved if I turn the
Container = styled.div
that I have as a wrapper into a normal, non-styled-componentdiv
.Edit:
And I also share your frustrations. It would be really nice if these two libraries worked together flawlessly. This issue and the specificity issue are two things that I hope get ironed out soon but I am grateful for the people volunteering their time.
Edit2:
I Just discovered what was causing the problem in my case: declaring styled-component components in the render method! This was causing the components to be recreated on every re-render and React couldn't track which element had focus across the re-render boundary. This was also leading to a few other re-render issues for me.
Moving all my styled-component declarations to outside of my component resolved the losing focus issues for me. Probably a noob mistake but no one has mentioned that here so I thought I would come back and report.