Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.js - input losing focus when rerendering

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

});
like image 828
Krab Avatar asked Oct 09 '22 13:10

Krab


People also ask

Why does Input lose focus?

it is because you are rendering the form in a function inside render(). Every time your state/prop change, the function returns a new form. it caused you to lose focus. Try putting what's inside the function into your render directly.

How do you keep input focused React?

To set focus on an input field after rendering with React, we can assign a ref to the input element with the useRef hook. Then we call focus on the current value of the ref to focus on the input. to call useRef to create a ref and assign it to inputReference . Then we call inputReference.

How do I stop component Rerendering in React?

1. Memoization using useMemo() and UseCallback() Hooks. Memoization enables your code to re-render components only if there's a change in the props. With this technique, developers can avoid unnecessary renderings and reduce the computational load in applications.

When Rerendering happens in React?

React components automatically re-render whenever there is a change in their state or props. A simple update of the state, from anywhere in the code, causes all the User Interface (UI) elements to be re-rendered automatically.


2 Answers

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.

like image 128
Mark Bolusmjak Avatar answered Oct 22 '22 18:10

Mark Bolusmjak


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

like image 97
Deepak Avatar answered Oct 22 '22 17:10

Deepak