React.js - input losing focus when rerendering

后端 未结 19 1920
猫巷女王i
猫巷女王i 2020-12-05 01:49

I am just writing to text input and in onChange event i call setState, so React rerenders my UI. The problem is that the text input always lose a f

相关标签:
19条回答
  • 2020-12-05 02:11

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

    ...
    // before
    <input value={myVar} />
    
    // after
    <input defaultValue={myVar} />
    
    0 讨论(0)
  • 2020-12-05 02:12

    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.

    0 讨论(0)
  • 2020-12-05 02:13

    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

    0 讨论(0)
  • 2020-12-05 02:13

    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.

    0 讨论(0)
  • 2020-12-05 02:15

    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.

    0 讨论(0)
  • 2020-12-05 02:16

    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.

    0 讨论(0)
提交回复
热议问题