My goal here is to create a form, which tabs you to the next input element when you hit the return key and submits the form when you are on the last input.
This is being built for mobile, and since there is no option to use the 'next' button instead of the 'go keyboard' button in browser (for more information about this see this answer).
To better visualize this, here is a picture:
I've also written some code, but the point here is that I am not able to catch the event in the right place, so the form gets immediately submitted after the return or when I prevent the event, the focus is changed after I hit return 2 times.
See the example here: https://codepen.io/ofhouse/pen/Rgwzxy (I've also pasted the code below)
class TextInput extends React.Component {
constructor(props) {
super(props);
this._onKeyPress = this._onKeyPress.bind(this);
}
componentDidMount() {
if (this.props.focus) {
this.textInput.focus();
}
}
componentDidUpdate(nextProps) {
if (nextProps.focus) {
this.textInput.focus();
}
}
_onKeyPress(e) {
if (e.key === 'Enter') {
this.props.onSubmit(e);
}
}
render() {
return (
<div>
<input
type="text"
onKeyPress={this._onKeyPress}
ref={input => {
this.textInput = input;
}}
/>
</div>
);
}
}
class Application extends React.Component {
constructor(props) {
super(props);
this.state = {
currentElement: 0,
};
}
_submitForm(e) {
// If I remove this preventDefault it works, but also the form is submitted --> SiteReload
e.preventDefault();
}
_changeFocus(nextElement) {
return e => {
this.setState({
currentElement: nextElement,
});
};
}
render() {
const { currentElement } = this.state;
return (
<form>
<h1>React input focus</h1>
<TextInput focus={currentElement === 0} onSubmit={this._changeFocus(1)} />
<TextInput focus={currentElement === 1} onSubmit={this._changeFocus(0)} />
<div>
<button type="submit" onClick={this._submitForm}>Submit</button>
</div>
</form>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
I don't think you're using the best approach, let me explain. The focusing of inputs is done by the focus
method of the native DOM element. To know what input to focus based on which input has the current focus is a logic that has to be implemented in the container of those inputs, the Application
component in your case.
I've made some significant changes to your code and now it's working: CodePen
Let me explain it:
First of all, we're preventing the submit of the keyPressed
event of the input when the key pressed is 'Enter'
, to prevent the form submit
_onKeyPress(e) {
if (e.key === 'Enter') {
this.props.onSubmit(e);
e.preventDefault();
}
}
We don't need either componenDidMount
nor componentDidUpdate
in TextInput
all we need is a focus
method:
focus() {
this.textInput.focus();
}
Most of the changes are made in the Application
component. First of all, we don't need the state, what we really need is to have the inputs in an array so we can call focus
on them.
constructor(props) {
super(props);
this.inputs = [];
}
To change the focus, we just need the index of the input to call the focus
method of the TextInput
component:
_changeFocus(index) {
return e => {
this.inputs[index].focus();
};
}
Then we need a way to add the inputs into this.inputs
, the ref
property would be ideal, I've create the method _addInput
as a helper for this:
_addInput(index) {
return input => {
this.inputs[index] = input;
};
}
Finally, when rendering the TextInput
s you need to pass them _addInput
to ref
and _changeFocus
to onSubmit
, with their respective indexes:
<TextInput ref={this._addInput(0)} onSubmit={this._changeFocus(1)} />
<TextInput ref={this._addInput(1)} onSubmit={this._changeFocus(0)} />
In the second TextInput
I'm changing the focus to the first one, but maybe submitting the form would be more useful.