React.js loses input focus on typing
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