Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TextField] loses focus on rerender #783

Closed
domachine opened this issue Jun 8, 2015 · 49 comments
Closed

[TextField] loses focus on rerender #783

domachine opened this issue Jun 8, 2015 · 49 comments
Labels
support: question Community support but can be turned into an improvement v0.x

Comments

@domachine
Copy link

If a textfield that had focus, is rerendered by setState, it looses focus.

@hai-cea hai-cea changed the title textfield looses focus on rerender [TextField] loses focus on rerender Jun 19, 2015
@coryasato
Copy link

+1

I have to use the onBlur event with defaultValue to get the Textfields working smoothly.

@aluksidadi
Copy link

anyone have a workaround to make this work?

@shaurya947
Copy link
Contributor

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:

4uo63bzhmj

@gmadar
Copy link

gmadar commented May 16, 2016

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}/>

@liddellj
Copy link

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).

@liddellj
Copy link

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.

@deyceg
Copy link

deyceg commented Jun 26, 2016

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.

@aesteve
Copy link

aesteve commented Jun 29, 2016

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.

@dalexander01
Copy link

dalexander01 commented Jul 28, 2016

Any work around to this? Also noticing this when using a regular input text area inside of a table.

@deyceg
Copy link

deyceg commented Jul 29, 2016

@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!

@Jbarget
Copy link

Jbarget commented Aug 5, 2016

@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>

@ausrasul
Copy link

ausrasul commented Oct 17, 2016

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));

@manuca
Copy link

manuca commented Mar 3, 2017

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.

@necronet
Copy link

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.

out

@cavaloni
Copy link

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.

@JacobSiegel35
Copy link

Still getting this error. Any work arounds?

@Aspintyl000
Copy link

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.

@JacobSiegel35
Copy link

@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.

@benjaminhon
Copy link

i am using textfields in a table row, and this is a problem :( any progress?

@sugandhhgoyal
Copy link

sugandhhgoyal commented Jul 24, 2017

Psid.txt

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.

@Garrik-Liu
Copy link

Garrik-Liu commented Aug 8, 2017

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 , when I click it, set the table's selectable=false. After your input operation, use an onBlue event to set the table's selectable=true.

@scotmatson
Copy link

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.

@spurton
Copy link

spurton commented Dec 17, 2017

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...

@JarLowrey
Copy link

JarLowrey commented Jan 4, 2018

@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.

@elliothimmelfarb
Copy link

elliothimmelfarb commented Jan 22, 2018

@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.

@piltsen
Copy link

piltsen commented Apr 12, 2018

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 (in this case).

So before, the structure was -> function Steps(props) -> Mapping Class . So now, I removed the function (the middle man) and just Const Steps -> Mapping Class . I can only speculate as to why this is working (perhaps some props were being ignored) but it solved my case.

@reecefenwick
Copy link

reecefenwick commented Aug 17, 2018

@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

@nthgol
Copy link

nthgol commented Dec 16, 2018

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}/>}
/>

@ghost
Copy link

ghost commented Dec 17, 2018

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.

@ArcanoxDragon
Copy link

@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 TextFields over and over again fixed it.

Can this be re-opened, or should a new issue be filed?

@OmarDavilaP
Copy link

I'm still getting this issue, I'm using 3.2.0

@joshwooding
Copy link
Member

@OmarDavilaP Can you create a new issue with a reproduction?

@OmarDavilaP
Copy link

OmarDavilaP commented May 22, 2019

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.

@fleischman718
Copy link

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

@oliviertassinari oliviertassinari added v0.x support: question Community support but can be turned into an improvement labels Jan 3, 2020
@noconsulate
Copy link

noconsulate commented Feb 14, 2020

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.

@leoncvlt
Copy link

leoncvlt commented Apr 8, 2020

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

@leoncvlt
Copy link

leoncvlt commented Apr 8, 2020

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}
    });
  };

@leoncvlt
Copy link

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

@gapinto
Copy link

gapinto commented Apr 18, 2020

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!

@mycoberago
Copy link

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!

@colinchristo4
Copy link

colinchristo4 commented Aug 6, 2020

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>
  )
}

@sophiagatliff
Copy link

Issue for me was styled components.

Used standard CSS for for textarea AND the enclosing div and it works fine.

@luisanton-io
Copy link

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

And you, sir, are a genius. Thanks.

@alexandervandekleutab
Copy link

@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.

This solved it for me. I was using a styled tooltip like

const DarkBackgroundTooltip = withStyles({
  tooltip: {
    color: '#FFFFFF',
    backgroundColor: '#000F33',
    width: '400px',
  },
})(Tooltip)

But that declaration was inside the component. I moved it out of the component and it works. Thanks!

@thisisglee
Copy link

Addind autoFocus worked for me
<TextField autoFocus fullWidth variant="filled" minRows={6} value={taxonomyInput} onChange={taxonomyInputHandler} />

@renbesson
Copy link

I've had this issue for days. Just today the dumb here realized that an useEffect inside the component was doing the rerenders.

@sachin-digi

This comment was marked as resolved.

@candiedoperation
Copy link

Addind autoFocus worked for me <TextField autoFocus fullWidth variant="filled" minRows={6} value={taxonomyInput} onChange={taxonomyInputHandler} />

This won't work if you have multiple fields...

@jennyvallon
Copy link

Anyone who is still running into this issue: please refer to #783 (comment) Edit 2. Putting styled components outside of the render method did the trick for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
support: question Community support but can be turned into an improvement v0.x
Projects
None yet
Development

No branches or pull requests