React.js - input losing focus when rerendering

JavascriptReactjs

Javascript Problem Overview


I am just writing to text input and in onChange event I call setState, so React re-renders my UI. The problem is that the text input always loses focus, so I need to focus it again for each letter :D.

var EditorContainer = React.createClass({

    componentDidMount: function () {
        $(this.getDOMNode()).slimScroll({height: this.props.height, distance: '4px', size: '8px'});
    },

    componentDidUpdate: function () {
        console.log("zde");
        $(this.getDOMNode()).slimScroll({destroy: true}).slimScroll({height: 'auto', distance: '4px', size: '8px'});
    },

    changeSelectedComponentName: function (e) {
        //this.props.editor.selectedComponent.name = $(e.target).val();
        this.props.editor.forceUpdate();
    },

    render: function () {

        var style = {
            height: this.props.height + 'px'
        };
        return (
            <div className="container" style={style}>
                <div className="row">
                    <div className="col-xs-6">
                    {this.props.selected ? <h3>{this.props.selected.name}</h3> : ''}
                    {this.props.selected ? <input type="text" value={this.props.selected.name} onChange={this.changeSelectedComponentName} /> : ''}
                    </div>
                    <div className="col-xs-6">
                        <ComponentTree editor={this.props.editor} components={this.props.components}/>
                    </div>
                </div>
            </div>
        );
    }

});

Javascript Solutions


Solution 1 - Javascript

Without seeing the rest of your code, this is a guess. When you create a EditorContainer, specify a unique key for the component:

<EditorContainer key="editor1"/>

When a re-rendering occurs, if the same key is seen, this will tell React don't clobber and regenerate the view, instead reuse. Then the focused item should retain focus.

Solution 2 - Javascript

I keep coming back here again and again and always find the solution to my elsewhere at the end. So, I'll document it here because I know I will forget this again!

The reason input was losing focus in my case was due to the fact that I was re-rendering the input on state change.

Buggy Code:

import React from 'react';
import styled from 'styled-components';

class SuperAwesomeComp extends React.Component {
  state = {
    email: ''
  };
  updateEmail = e => {
    e.preventDefault();
    this.setState({ email: e.target.value });
  };
  render() {
    const Container = styled.div``;
    const Input = styled.input``;
    return (
      <Container>
        <Input
          type="text"
          placeholder="Gimme your email!"
          onChange={this.updateEmail}
          value={this.state.email}
        />
      </Container>
    )
  }
}

So, the problem is that I always start coding everything at one place to quickly test and later break it all into separate modules. But, here this strategy backfires because updating the state on input change triggers render function and the focus is lost.

Fix is simple, do the modularization from the beginning, in other words, "Move the Input component out of render function"

Fixed Code

import React from 'react';
import styled from 'styled-components';

const Container = styled.div``;
const Input = styled.input``;

class SuperAwesomeComp extends React.Component {
  state = {
    email: ''
  };
  updateEmail = e => {
    e.preventDefault();
    this.setState({ email: e.target.value });
  };
  render() {
    return (
      <Container>
        <Input
          type="text"
          placeholder="Gimme your email!"
          onChange={this.updateEmail}
          value={this.state.email}
        />
      </Container>
    )
  }
}

Ref. to the solution: https://github.com/styled-components/styled-components/issues/540#issuecomment-283664947

Solution 3 - Javascript

If it's a problem within a react router <Route/> use the render prop instead of component.

<Route path="/user" render={() => <UserPage/>} />


The loss of focus happens because the component prop uses React.createElement each time instead of just re-rendering the changes.

Details here: https://reacttraining.com/react-router/web/api/Route/component

Solution 4 - Javascript

I had the same symptoms with hooks. Yet my problem was defining a component inside the parent.

Wrong:

const Parent =() => {
    const Child = () => <p>Child!</p>
    return <Child />
}

Right:

const Child = () => <p>Child!</p>
const Parent = () => <Child />

Solution 5 - Javascript

My answer is similar to what @z5h said.

In my case, I used Math.random() to generate a unique key for the component.

I thought the key is only used for triggering a rerender for that particular component rather than re-rendering all the components in that array (I return an array of components in my code). I didn't know it is used for restoring the state after rerendering.

Removing that did the job for me.

Solution 6 - Javascript

Applying the autoFocus attribute to the input element can perform as a workaround in situations where there's only one input that needs to be focused. In that case a key attribute would be unnecessary because it's just one element and furthermore you wouldn't have to worry about breaking the input element into its own component to avoid losing focus on re-render of main component.

Solution 7 - Javascript

I got the same behavior.

The problem in my code was that i created a nested Array of jsx elements like this:

const example = [
            [
                <input value={'Test 1'}/>,
                <div>Test 2</div>,
                <div>Test 3</div>,
            ]
        ]

...

render = () => {
    return <div>{ example }</div>
}

Every element in this nested Array re-renders each time I updated the parent element. And so the inputs lose there "ref" prop every time

I fixed the Problem with transform the inner array to a react component (a function with a render function)

const example = [
            <myComponentArray />
        ]

 ...

render = () => {
    return <div>{ example }</div>
}

EDIT:

The same issue appears when i build a nested React.Fragment

const SomeComponent = (props) => (
    <React.Fragment>
        <label ... />
        <input ... />
    </React.Fragment>
);

const ParentComponent = (props) => (
    <React.Fragment>
        <SomeComponent ... />
        <div />
    </React.Fragment>
);

Solution 8 - Javascript

The answers supplied didn't help me, here was what I did but I had a unique situation.

To clean up the code I tend to use this format until I'm ready to pull the component into another file.

render(){
   const MyInput = () => {
      return <input onChange={(e)=>this.setState({text: e.target.value}) />
   }
   return(
      <div>
         <MyInput />
      </div>
   )

But this caused it to lose focus, when I put the code directly in the div it worked.

return(
   <div>
      <input onChange={(e)=>this.setState({text: e.target.value}) />
   </div>
)

I don't know why this is, this is the only issue I've had with writing it this way and I do it in most files I have, but if anyone does a similar thing this is why it loses focus.

Solution 9 - Javascript

If the input field is inside another element (i.e., a container element like <div key={"bart"}...><input key={"lisa"}...> ... </input></div>-- the ellipses here indicating omitted code), there must be a unique and constant key on the container element (as well as on the input field). Elsewise, React renders up a brand new container element when child's state is updated rather than merely re-rendering the old container. Logically, only the child element should be updated, but...

I had this problem while trying to write a component that took a bunch of address information. The working code looks like this

// import react, components
import React, { Component } from 'react'

// import various functions
import uuid from "uuid";

// import styles
import "../styles/signUp.css";

export default class Address extends Component {
  constructor(props) {
    super(props);
    this.state = {
      address1: "",
      address2: "",
      address1Key: uuid.v4(),
      address2Key: uuid.v4(),
      address1HolderKey: uuid.v4(),
      address2HolderKey: uuid.v4(),
      // omitting state information for additional address fields for brevity
    };
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(event) {
    event.preventDefault();
    this.setState({ [`${event.target.id}`]: event.target.value })
  }

  render() {
    return (
      <fieldset>
        <div className="labelAndField" key={this.state.address1HolderKey} >
          <label className="labelStyle" for="address1">{"Address"}</label>
          <input className="inputStyle"
            id="address1"
            name="address1"
            type="text"
            label="address1"
            placeholder=""
            value={this.state.address1}
            onChange={this.handleChange}
            key={this.state.address1Key} ></input >
        </div> 
        <div className="labelAndField" key={this.state.address2HolderKey} >
          <label className="labelStyle" for="address2">{"Address (Cont.)"}</label>
          <input className="inputStyle"
            id="address2"
            name="address2"
            type="text"
            label="address2"
            placeholder=""
            key={this.state.address2Key} ></input >
        </div>
        {/* omitting rest of address fields for brevity */}
      </fieldset>
    )
  }
}

Sharp-eyed readers will note that <fieldset> is a containing element, yet it doesn't require a key. The same holds for <> and <React.Fragment> or even <div> Why? Maybe only the immediate container needs a key. I dunno. As math textbooks say, the explanation is left to the reader as an exercise.

Solution 10 - Javascript

I solved the same issue deleting the key attribute in the input and his parent elements

// Before
<input
    className='invoice_table-input invoice_table-input-sm'
    type='number'
    key={ Math.random }
    defaultValue={pageIndex + 1}
    onChange={e => {
        const page = e.target.value ? Number(e.target.value) - 1 : 0
        gotoPage(page)
    }}
/>
// After
<input
    className='invoice_table-input invoice_table-input-sm'
    type='number'
    defaultValue={pageIndex + 1}
    onChange={e => {
        const page = e.target.value ? Number(e.target.value) - 1 : 0
        gotoPage(page)
    }}
/>

Solution 11 - Javascript

I had this issue and the problem turned out to be that I was using a functional component and linking up with a parent component's state. If I switched to using a class component, the problem went away. Hopefully there is a way around this when using functional components as it's a lot more convenient for simple item renderers et al.

Solution 12 - Javascript

What I did was just change the value prop to defaultValue and second change was onChange event to onBlur.

Solution 13 - Javascript

I just ran into this issue and came here for help. Check your CSS! The input field cannot have user-select: none; or it won't work on an iPad.

Solution 14 - Javascript

For me, this was being caused by the search input box being rendered in the same component (called UserList) as the list of search results. So whenever the search results changed, the whole UserList component rerendered, including the input box.

My solution was to create a whole new component called UserListSearch which is separate from UserList. I did not need to set keys on the input fields in UserListSearch for this to work. The render function of my UsersContainer now looks like this:

class UserContainer extends React.Component {
  render() {
    return (
      <div>
        <Route
          exact
          path={this.props.match.url}
          render={() => (
            <div>
              <UserListSearch
                handleSearchChange={this.handleSearchChange}
                searchTerm={this.state.searchTerm}
              />
              <UserList
                isLoading={this.state.isLoading}
                users={this.props.users}
                user={this.state.user}
                handleNewUserClick={this.handleNewUserClick}
              />
            </div>
          )}
        />
      </div>  
    )
  }
}

Hopefully this helps someone too.

Solution 15 - Javascript

I switched value prop to defaultValue. That works for me.

...
// before
<input value={myVar} />

// after
<input defaultValue={myVar} />

Solution 16 - Javascript

My problem was that I named my key dynamically with a value of the item, in my case "name" so the key was key={${item.name}-${index}}. So when I wanted to change the input with item.name as the value, they key would also change and therefore react would not recognize that element

Solution 17 - Javascript

included the next code in tag input:

ref={(input) => {
     if (input) {
         input.focus();
     }
 }}

Before:

<input
      defaultValue={email}
      className="form-control"
      type="email"
      id="email"
      name="email"
      placeholder={"[email protected]"}
      maxLength="15"
      onChange={(e) => validEmail(e.target.value)}
/>

After:

<input
     ref={(input) => {
          if (input) {
             input.focus();
          }
      }}
      defaultValue={email}
      className="form-control"
      type="email"
      id="email"
      name="email"
      placeholder={"[email protected]"}
      maxLength="15"
      onChange={(e) => validEmail(e.target.value)}
/>

Solution 18 - Javascript

The core reason is: When React re-render, your previous DOM ref will be invalid. It mean react has change the DOM tree, and you this.refs.input.focus won't work, because the input here doesn't exist anymore.

Solution 19 - Javascript

I had the same problem with an html table in which I have input text lines in a column. inside a loop I read a json object and I create rows in particular I have a column with inputtext.

http://reactkungfu.com/2015/09/react-js-loses-input-focus-on-typing/

I managed to solve it in the following way

import { InputTextComponent } from './InputTextComponent';
//import my  inputTextComponent 
...

var trElementList = (function (list, tableComponent) {

    var trList = [],
        trElement = undefined,
        trElementCreator = trElementCreator,
        employeeElement = undefined;



    // iterating through employee list and
    // creating row for each employee
    for (var x = 0; x < list.length; x++) {

        employeeElement = list[x];
        
        var trNomeImpatto = React.createElement('tr', null, <td rowSpan="4"><strong>{employeeElement['NomeTipologiaImpatto'].toUpperCase()}</strong></td>);
        trList.push(trNomeImpatto);

        trList.push(trElementCreator(employeeElement, 0, x));
        trList.push(trElementCreator(employeeElement, 1, x));
        trList.push(trElementCreator(employeeElement, 2, x));
        
    } // end of for  

    return trList; // returns row list

    function trElementCreator(obj, field, index) {
        var tdList = [],
            tdElement = undefined;
        
        //my input text
        var inputTextarea = <InputTextComponent
            idImpatto={obj['TipologiaImpattoId']}//index
            value={obj[columns[field].nota]}//initial value of the input I read from my json data source
            noteType={columns[field].nota}
            impattiComposite={tableComponent.state.impattiComposite}
            //updateImpactCompositeNote={tableComponent.updateImpactCompositeNote}
        />
      
        tdElement = React.createElement('td', { style: null }, inputTextarea);
        tdList.push(tdElement);
       

        var trComponent = createClass({

            render: function () {
                return React.createElement('tr', null, tdList);
            }
        });
        return React.createElement(trComponent);
    } // end of trElementCreator

});
...    
    //my tableComponent
    var tableComponent = createClass({
        // initial component states will be here
        // initialize values
        getInitialState: function () {
            return {
                impattiComposite: [],
                serviceId: window.sessionStorage.getItem('serviceId'),
                serviceName: window.sessionStorage.getItem('serviceName'),
                form_data: [],
                successCreation: null,
            };
        },
    
        //read a json data soure of the web api url
        componentDidMount: function () {
            this.serverRequest =
                $.ajax({
                    url: Url,
                    type: 'GET',
                    contentType: 'application/json',
                    data: JSON.stringify({ id: this.state.serviceId }),
                    cache: false,
                    success: function (response) {
                        this.setState({ impattiComposite: response.data });
                    }.bind(this),
    
                    error: function (xhr, resp, text) {
                        // show error to console
                        console.error('Error', xhr, resp, text)
                        alert(xhr, resp, text);
                    }
                });
        },
    
        render: function () {
    ...
    React.createElement('table', {style:null}, React.createElement('tbody', null,trElementList(this.state.impattiComposite, this),))
    ...
    }


    
            //my input text
            var inputTextarea = <InputTextComponent
                        idImpatto={obj['TipologiaImpattoId']}//index
                        value={obj[columns[field].nota]}//initial value of the input I read //from my json data source
                        noteType={columns[field].nota}
                        impattiComposite={tableComponent.state.impattiComposite}//impattiComposite  = my json data source
                 
                    />//end my input text
            
                    tdElement = React.createElement('td', { style: null }, inputTextarea);
                    tdList.push(tdElement);//add a component
        
        //./InputTextComponent.js
        import React from 'react';
        
        export class InputTextComponent extends React.Component {
          constructor(props) {
            super(props);
            this.state = {
              idImpatto: props.idImpatto,
              value: props.value,
              noteType: props.noteType,
              _impattiComposite: props.impattiComposite,
            };
            this.updateNote = this.updateNote.bind(this);
          }
        
        //Update a inpute text with new value insert of the user
    
          updateNote(event) {
            this.setState({ value: event.target.value });//update a state of the local componet inputText
            var impattiComposite = this.state._impattiComposite;
            var index = this.state.idImpatto - 1;
            var impatto = impattiComposite[index];
            impatto[this.state.noteType] = event.target.value;
            this.setState({ _impattiComposite: impattiComposite });//update of the state of the father component (tableComponet)
    
          }
        
          render() {
            return (
              <input
                className="Form-input"
                type='text'
                value={this.state.value}
                onChange={this.updateNote}>
              </input>
            );
          }
        }

Solution 20 - Javascript

Simple solution in my case:

<input ref={ref => ref && ref.focus()}
    onFocus={(e)=>e.currentTarget.setSelectionRange(e.currentTarget.value.length, e.currentTarget.value.length)}
    />

ref triggers focus, and that triggers onFocus to calculate the end and set the cursor accordingly.

Solution 21 - Javascript

The issue in my case was that the key prop values I was setting on the InputContainer component and the input fields themselves were generated using Math.random(). The non-constant nature of the values made it hard for track to be kept of the input field being edited.

Solution 22 - Javascript

Turns out I was binding this to the component which was causing it to rerender.

I figured I'd post it here in case anyone else had this issue.

I had to change

<Field
    label="Post Content"
    name="text"
    component={this.renderField.bind(this)}
/>

To

<Field
    label="Post Content"
    name="text"
    component={this.renderField}
/>

Simple fix since in my case, I didn't actually need this in renderField, but hopefully me posting this will help someone else.

Solution 23 - Javascript

For me I had a text area inside a portal. This text area was loosing focus. My buggy portal implementation was like this:

export const Modal = ({children, onClose}: modelProps) => {
  const modalDOM = document.getElementById("modal");
  const divRef = useRef(document.createElement('div')); 

  useEffect(()=>{
    const ref = divRef.current;
    modalDOM?.appendChild(ref);
    return ()=>{
      modalDOM?.removeChild(ref);
    }
  });

  const close = (e: React.MouseEvent) => {
    e.stopPropagation();
    onClose();
  };

  const handleClick = (e: React.MouseEvent) => {
    e.stopPropagation()
  }

  return (
    createPortal(
    <div className="modal" onClick={close}>
      <div className="modal__close-modal" onClick={close}>x</div>
      {children}
    </div>, 
    divRef.current)
  )
}

const Parent = ({content: string}: ParentProps) => {
  const [content, setContent] = useState<string>(content);
  
  const onChangeFile = (e: React.MouseEvent) => {
    setContent(e.currentTarget.value);
  }

  return (
    <Modal>
      <textarea
        value={content}
        onChange={onChangeFile}>
      </textarea>
    </Modal>
    )
  }

Turned out following implementation worked correctly, here I am directly attaching modal component to the DOM element.

export const Modal = ({children, onClose}: modelProps) => {
  const modalDOM = document.getElementById("modal");

  const close = (e: React.MouseEvent) => {
    e.stopPropagation();
    onClose();
  };

  return (
    createPortal(
      <div className="modal" onClick={close}>
        <div className="modal__close-modal" onClick={close}>x</div>
          {children}
        </div>, 
        modalDOM || document.body)
    )
  }

Solution 24 - Javascript

Changing text in the input of some control can cause parent control rerendering in some cases (according to binding to props). In this case focus will be lost. Editing should not has effect to parent container in DOM.

Solution 25 - Javascript

I had a similar issue, this is fixed it.

const component = () => {
 return <input onChange={({target})=>{
    setValue(target.vlaue)
   }       
 } />
}

const ThisComponentKeptRefreshingContainer = () => {
   return(
     <component />
   )
}

const ThisContainerDidNot= () => {
   return(
     <> {component()} </>
   )
}

As the code illustrate calling the component child like an element gave that re-rendering effect, however, calling it like a function did not. hope it helps someone

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKrabView Question on Stackoverflow
Solution 1 - JavascriptMark BolusmjakView Answer on Stackoverflow
Solution 2 - JavascriptDeepakView Answer on Stackoverflow
Solution 3 - Javascriptspencer.smView Answer on Stackoverflow
Solution 4 - JavascriptilkerkaranView Answer on Stackoverflow
Solution 5 - JavascriptShankar ThyagarajanView Answer on Stackoverflow
Solution 6 - JavascriptspakmadView Answer on Stackoverflow
Solution 7 - JavascriptchrisheynView Answer on Stackoverflow
Solution 8 - JavascriptKevin DanikowskiView Answer on Stackoverflow
Solution 9 - JavascriptCharles GoodwinView Answer on Stackoverflow
Solution 10 - JavascriptAdrián PedreiraView Answer on Stackoverflow
Solution 11 - JavascriptKyle CrossmanView Answer on Stackoverflow
Solution 12 - JavascriptManal LiaquatView Answer on Stackoverflow
Solution 13 - JavascriptDavid SinclairView Answer on Stackoverflow
Solution 14 - JavascriptDom EdenView Answer on Stackoverflow
Solution 15 - JavascriptchrisheynView Answer on Stackoverflow
Solution 16 - JavascriptAmoguView Answer on Stackoverflow
Solution 17 - JavascriptThiago PiresView Answer on Stackoverflow
Solution 18 - JavascripthlissnakeView Answer on Stackoverflow
Solution 19 - JavascriptSoftware FactoryView Answer on Stackoverflow
Solution 20 - JavascriptHarley LangView Answer on Stackoverflow
Solution 21 - JavascriptRomeo FolieView Answer on Stackoverflow
Solution 22 - JavascriptMattView Answer on Stackoverflow
Solution 23 - JavascriptSumit KushwahaView Answer on Stackoverflow
Solution 24 - JavascriptSalientBrainView Answer on Stackoverflow
Solution 25 - JavascriptProauView Answer on Stackoverflow