I have a React (15.3.2) component with a text input.
(Everywhere I say "render" here it's actually render or unhide; I've tried both.)
When that input element is blurred, I render a new component with a text input.
I want give the new text input focus.
I've tried componentDidMount
, componentWillUpdate
, and componentDidUpdate
; I've tried named and function ref
s; I've tried react-dom.
The focusing itself works, e.g., once it's been rendered, if I click in the initial input, focus goes to the new input (this is a bug, but compared to focusing, trivial).
The first input has an onBlur
that sets the state used to tell the second input to render or not.
In that blur handler I stop the event as best as I can.
When I tab out of the first element I'm already "past" the newly-rendered element, e.g., the browser tab bar in my current bare design–I guess the new element hasn't been rendered yet?
class SecondInput extends Component {
componentDidUpdate = (prevProps, prevState) => {
if (!this.props.hidden) this._input.focus()
}
render = () =>
<input type="text" hidden={this.props.hidden} ref={(c) => this._input = c}
}
class NewItem extends Component {
state = { itemEntered: false }
itemBlurred = (e) => {
e.preventDefault()
e.stopPropagation()
this.setState({ itemEntered: true })
}
render = () =>
<div>
Item: <input type="text" onBlur={this.itemBlurred} />
<SecondInput hidden={!this.state.itemEntered} />
</div>
}
Any ideas or hints? I have to believe it's something obvious, because surely this happens all the time.
I'm also open to any other form of component hierarchy, e.g., if I need to have a container that wraps all this stuff up somehow that's fine.
React 15.3.2
The problem you are seeing appears to be because there are no more focusable elements on the page when you press tab, so the focus goes to the address bar. For some reason when the focus is on the address bar, just calling this._input.focus()
does not grab focus as you would expect.
In order to combat this problem, I have added an empty div, and set the tabIndex
property based on whether or not the second input is shown.
Just to make things easier for myself, I made the input focus on mount instead of using the hidden property. This may or may not work in your case, but it seemed to be cleaner since it would keep the input from calling focus on every keypress if it were to be a controlled input.
let Component = React.Component;
class SecondInput extends Component {
componentDidMount(){
this.textInput.focus()
}
render(){
return (
<input type="text" ref={(input) => this.textInput = input} />
)
}
}
class NewItem extends Component {
state = { itemEntered: false }
itemBlurred = (e) => {
//e.preventDefault()
//e.stopPropagation()
this.setState({ itemEntered: true })
}
render = () =>
<div>
Item: <input type="text" onBlur={this.itemBlurred} />
{
this.state.itemEntered ? [
<SecondInput/>
] : []
}
<div tabIndex={this.state.itemEntered ? null : 0}/>
</div>
}
ReactDOM.render(<NewItem />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.0/react-dom.min.js"></script>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With