最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

javascript - reactjs input element loses focus after keystroke - Stack Overflow

matteradmin7PV0评论

So I am using a hash to store the values of dynamically created rows of input values and I lose focus on the input I am modifying after entering only one character. I think the solution to this may be to use refs to refocus on only the last input changed, but I couldn't get it to work, as I wasn't able to figure out how to specify which element was last changed. Advice on how to solve this is appreciated.

The code below dynamically creates input boxes, and looks up their values based on the unitPriceValueHash. Each variant has an id, and id is used as the key to the hash.

I created a codepen to try and recreate the problem, but the issue im facing doesn't show up in code pen. In my actual app I press 1 for example in the input box, then the cursor is not on the input box anymore.

The only difference between the codepen and my code appears to be the fact the the inputs are nested inside a table.

  CreateItem(variant) {
    const unitPriceValueHash = this.props.unitPriceValueHash
    return { 
      variant_title: variant.variant_title,
      variant_price: variant.variant_price,
      unit_cost: <TextField 
                  type="number"
                  onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
                  key={variant.id}
                  value={unitPriceValueHash[variant.id] || ''}
                 />
    };
  }

Below is the change of state that modifies the hash

  handleUnitPriceChange (id, event) {
    const unitPriceValueHash = this.state.unitPriceValueHash
    unitPriceValueHash[id] = event
    console.log(unitPriceValueHash)
    this.setState({unitPriceValueHash: unitPriceValueHash});
    //this.updateVariantUnitCost(id, event);
  }

So I am using a hash to store the values of dynamically created rows of input values and I lose focus on the input I am modifying after entering only one character. I think the solution to this may be to use refs to refocus on only the last input changed, but I couldn't get it to work, as I wasn't able to figure out how to specify which element was last changed. Advice on how to solve this is appreciated.

The code below dynamically creates input boxes, and looks up their values based on the unitPriceValueHash. Each variant has an id, and id is used as the key to the hash.

I created a codepen to try and recreate the problem, but the issue im facing doesn't show up in code pen. In my actual app I press 1 for example in the input box, then the cursor is not on the input box anymore.

https://codepen.io/ByteSize/pen/oogLpE?editors=1011

The only difference between the codepen and my code appears to be the fact the the inputs are nested inside a table.

  CreateItem(variant) {
    const unitPriceValueHash = this.props.unitPriceValueHash
    return { 
      variant_title: variant.variant_title,
      variant_price: variant.variant_price,
      unit_cost: <TextField 
                  type="number"
                  onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
                  key={variant.id}
                  value={unitPriceValueHash[variant.id] || ''}
                 />
    };
  }

Below is the change of state that modifies the hash

  handleUnitPriceChange (id, event) {
    const unitPriceValueHash = this.state.unitPriceValueHash
    unitPriceValueHash[id] = event
    console.log(unitPriceValueHash)
    this.setState({unitPriceValueHash: unitPriceValueHash});
    //this.updateVariantUnitCost(id, event);
  }
Share Improve this question edited Nov 6, 2017 at 23:28 ByteMe asked Oct 30, 2017 at 2:00 ByteMeByteMe 1,4022 gold badges18 silver badges31 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 1

There's a couple problems with the code you've shared.

  • Don't use inline functions. Each render, the function is created again which means that when react pares the props, it looks like the function is different (it is a new/different function each time!) and react will re-render.
  • Don't modify any objects which exist in the state, instead create a new object. If you modify an object that exists in the state, you're essentially saying you don't want renders to be consistent and reproducible.

I've re-posted your original code with the issues highlighted

CreateItem(variant) {
  const unitPriceValueHash = this.props.unitPriceValueHash
  return {
    variant_title: variant.variant_title,
    variant_price: variant.variant_price,
    unit_cost: <TextField
      type="number"
      onChange={(event) => this.handleUnitPriceChange(variant.id, event)}
      // ^^^^ - inline functions cause react to re-render every time, instead - create a ponent
      key={variant.id}
      value={unitPriceValueHash[variant.id] || ''}
    />
  };
}

handleUnitPriceChange(id, event) {
  const unitPriceValueHash = this.state.unitPriceValueHash
  unitPriceValueHash[id] = event
  // ^^^^ - please, please - don't do this. You can't mutate the state like this.

  // instead, do the following to create a new modified object without modifying the object in the state
  const unitPriceValueHash = Object.assign({}, this.state.unitPriceValueHash, { id: event });
  this.setState({ unitPriceValueHash: unitPriceValueHash });
}

In regards to the inline-function, generally the remendation is to create a new ponent for this which takes the value as a prop. That might look like this:

class UnitCost extends PureComponent {
  static propTypes = {
    variantId: PropTypes.number,
    variantValue: PropTypes.object,
    onUnitPriceChange: PropTypes.func,
  }
  handleUnitPriceChange(e) {
    this.props.onUnitPriceChange(this.props.variantId, e)
  }
  render() {
    return (
      <TextField
        type="number"
        onChange={this.handleUnitPriceChange}
        value={this.props.variantValue || ''}
      />
    );
  }
}

CreateItem(variant) {
  const unitPriceValueHash = this.props.unitPriceValueHash
  return {
    variant_title: variant.variant_title,
    variant_price: variant.variant_price,
    unit_cost: (
      <UnitCost
        key={variant.id}
        variantId={variant.id}
        variantValue={unitPriceValueHash[variant.id]}
        onUnitPriceChange={this.handleUnitPriceChange}
      />
    ),
  };
}

Regarding your concerns about focus, react generally won't lose your object focus when re-rendering, so don't ever, ever re-focus an object after an update for this reason.

The only time react will lose focus, is if it pletely discards the current DOM tree and starts over from scratch. It will do this if it thinks a parent object has been replaced instead of modified. This can happen because of a missing key prop, or a key prop that has changed.

You have not posted enough code for us to investigate this further. If you want more help you should build a minimum reproducible example that we can run and test.

The solution to this problem had me use an intermediate state to store the value of the input field on change, and a submit AJAX request on an onBlur

class TextFieldWrapper extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: this.props.variantValue[this.props.variantId] || '',
    }
    this.handleUnitPriceChange = this.handleUnitPriceChange.bind(this)
    this.updateValue = this.updateValue.bind(this)
  }
  updateValue(value){
    this.setState({
      value: value,
    });
  }
  handleUnitPriceChange() {
    this.props.onUnitPriceChange(this.props.variantId, this.state.value);
  }
  render(){
    return (
      <TextField
        type="number"
        id={this.props.variantId}
        key={this.props.variantId}
        onChange={this.updateValue}
        onBlur={this.handleUnitPriceChange}
        value={this.state.value}
      />
    );
  }
}
Post a comment

comment list (0)

  1. No comments so far