I recently stumbled upon an interesting problem. React.js was loosing focus on an input while the user was typing.

Here is a video of that problem.

The bug was very easy to fix. It turned out that the the input key had it’s value as a part of it.

for (var i = 0; i < recipients.length; i++) {
  children.push(
  <input
    key={['recipient', recipients[i], i].join('_')}
    type="email"
    value={recipients[i]}
    onChange={this.props.recipientEmailChangedHandler.bind(null, i)}
  />)
}

This meant that when the user typed one character the recipients[i] value changed (via recipientEmailChangedHandler). Then react re-rendered the page and updated the input. But because the key was different between re-renders it throws away the old input and adds a new input in its place. It looks identical, but it looses the focus.

You should never use value as part of the key when users can edit the value.

When debugging such kind of problems, it is worth watching the real DOM nodes in Chrome console. You can notice things which change and this can help you trace the issue.

Changing the key to be based simply on the text and number was sufficient to fix the problem.

for (var i = 0; i < recipients.length; i++) {
  children.push(
  <input
    key={['recipient', i].join('_')}
    type="email"
    value={recipients[i]}
    onChange={this.props.recipientEmailChangedHandler.bind(null, i)}
  />)
}

Also, remember that such problem can be caused by invalid keys higher in the hierarchy.

If the higher list component which contains all the recipient inputs used recipients.length as part of its key, then adding or removing recipients would cause losing focus as well. Becuase react would re-render the entire list when the key changed. Try to avoid such kind of mistakes. You don’t need to overcomplicate your keys. Keep them simple.

You can read more in React.js and Dynamic Children - Why the Keys are Important

Until next time, Robert

comments powered by Disqus