React Native: How to select the next TextInput after pressing the “next” keyboard button?
I defined two TextInput fields as follows:
<TextInput
style = {styles.titleInput}
returnKeyType = {"next"}
autoFocus = {true}
placeholder = "Title" />
<TextInput
style = {styles.descriptionInput}
multiline = {true}
maxLength = {200}
placeholder = "Description" />
But after pressing the "next" button on my keyboard, my react-native app isn't jumping to the second TextInput field. How can I achieve that?
Thanks!
Set the second TextInput
focus, when the previous TextInput
's onSubmitEditing
is triggered.
Try this
Adding a Ref to second TextInput
ref={(input) => { this.secondTextInput = input; }}
Bind focus function to first TextInput's onSubmitEditing event.
onSubmitEditing={() => { this.secondTextInput.focus(); }}
Remember to set blurOnSubmit to false, to prevent keyboard flickering.
blurOnSubmit={false}
When all done, it should looks like this.
<TextInput
placeholder = "FirstTextInput"
returnKeyType = { "next" }
onSubmitEditing={() => { this.secondTextInput.focus(); }}
blurOnSubmit={false}
/>
<TextInput
ref={(input) => { this.secondTextInput = input; }}
placeholder = "secondTextInput"
/>
You can do this without using refs. This approach is preferred, since refs can lead to fragile code. The React docs advise finding other solutions where possible:
If you have not programmed several apps with React, your first inclination is usually going to be to try to use refs to "make things happen" in your app. If this is the case, take a moment and think more critically about where state should be owned in the component hierarchy. Often, it becomes clear that the proper place to "own" that state is at a higher level in the hierarchy. Placing the state there often eliminates any desire to use refs to "make things happen" – instead, the data flow will usually accomplish your goal.
Instead, we'll use a state variable to focus the second input field.
Add a state variable that we'll pass as a prop to the
DescriptionInput
:initialState() { return { focusDescriptionInput: false, }; }
Define a handler method that will set this state variable to true:
handleTitleInputSubmit() { this.setState(focusDescriptionInput: true); }
Upon submitting / hitting enter / next on the
TitleInput
, we'll callhandleTitleInputSubmit
. This will setfocusDescriptionInput
to true.<TextInput style = {styles.titleInput} returnKeyType = {"next"} autoFocus = {true} placeholder = "Title" onSubmitEditing={this.handleTitleInputSubmit} />
DescriptionInput
'sfocus
prop is set to ourfocusDescriptionInput
state variable. So, whenfocusDescriptionInput
changes (in step 3),DescriptionInput
will re-render withfocus={true}
.<TextInput style = {styles.descriptionInput} multiline = {true} maxLength = {200} placeholder = "Description" focus={this.state.focusDescriptionInput} />
This is a nice way to avoid using refs, since refs can lead to more fragile code :)
EDIT: h/t to @LaneRettig for pointing out that you'll need to wrap the React Native TextInput with some added props & methods to get it to respond to focus
:
// Props:
static propTypes = {
focus: PropTypes.bool,
}
static defaultProps = {
focus: false,
}
// Methods:
focus() {
this._component.focus();
}
componentWillReceiveProps(nextProps) {
const {focus} = nextProps;
focus && this.focus();
}
As of React Native 0.36, calling focus()
(as suggested in several other answers) on a text input node isn't supported any more. Instead, you can use the TextInputState
module from React Native. I created the following helper module to make this easier:
// TextInputManager
//
// Provides helper functions for managing the focus state of text
// inputs. This is a hack! You are supposed to be able to call
// "focus()" directly on TextInput nodes, but that doesn't seem
// to be working as of ReactNative 0.36
//
import { findNodeHandle } from 'react-native'
import TextInputState from 'react-native/lib/TextInputState'
export function focusTextInput(node) {
try {
TextInputState.focusTextInput(findNodeHandle(node))
} catch(e) {
console.log("Couldn't focus text input: ", e.message)
}
}
You can, then, call the focusTextInput
function on any "ref" of a TextInput
. For example:
...
<TextInput onSubmit={() => focusTextInput(this.refs.inputB)} />
<TextInput ref="inputB" />
...
I created a small library that does this, no code change needed other than replacing your wrapping view and import of TextInput:
import { Form, TextInput } from 'react-native-autofocus'
export default () => (
<Form>
<TextInput placeholder="test" />
<TextInput placeholder="test 2" />
</Form>
)
https://github.com/zackify/react-native-autofocus
Explained in detail here: https://zach.codes/autofocus-inputs-in-react-native/
Using react-native 0.45.1 I also encountered problems trying to set focus on a password TextInput after pressing return key on a username TextInput.
After having tried most of the top rated solutions here on SO I found a solution on github that fulfilled my needs: https://github.com/shoutem/ui/issues/44#issuecomment-290724642
To sum it up:
import React, { Component } from 'react';
import { TextInput as RNTextInput } from 'react-native';
export default class TextInput extends Component {
render() {
const { props } = this;
return (
<RNTextInput
{...props}
ref={(input) => props.inputRef && props.inputRef(input)}
/>
);
}
}
And then I use it like this:
import React, {Component} from 'react';
import {
View,
} from 'react-native';
import TextInput from "../../components/TextInput";
class Login extends Component {
constructor(props) {
super(props);
this.passTextInput = null
}
render() {
return (
<View style={{flex:1}}>
<TextInput
style={{flex:1}}
placeholder="Username"
onSubmitEditing={(event) => {
this.passTextInput.focus()
}}
/>
<TextInput
style={{flex:1}}
placeholder="Password"
inputRef={(input) => {
this.passTextInput = input
}}
/>
</View>
)
}
}
If you happen to be using tcomb-form-native
as I am, you can do this, too. Here's the trick: instead of setting the props of the TextInput
directly, you do it via options
. You can refer to the fields of the form as:
this.refs.form.getComponent('password').refs.input.focus()
So the final product looks something like this:
var t = require('tcomb-form-native');
var Form = t.form.Form;
var MyForm = t.struct({
field1: t.String,
field2: t.String,
});
var MyComponent = React.createClass({
_getFormOptions () {
return {
fields: {
field1: {
returnKeyType: 'next',
onSubmitEditing: () => {this.refs.form.getComponent('field2').refs.input.focus()},
},
},
};
},
render () {
var formOptions = this._getFormOptions();
return (
<View style={styles.container}>
<Form ref="form" type={MyForm} options={formOptions}/>
</View>
);
},
});
(Credit to remcoanker for posting the idea here: https://github.com/gcanti/tcomb-form-native/issues/96)
For me on RN 0.50.3 it's possible with this way:
<TextInput
autoFocus={true}
onSubmitEditing={() => {this.PasswordInputRef._root.focus()}}
/>
<TextInput ref={input => {this.PasswordInputRef = input}} />
You must see this.PasswordInputRef._root.focus()
Try this solution on React Native's GitHub issues.
https://github.com/facebook/react-native/pull/2149#issuecomment-129262565
You need to use the ref prop for the TextInput component.
Then you need a create a function that gets called on onSubmitEditing prop that moves the focus on the second TextInput ref.
var InputScreen = React.createClass({
_focusNextField(nextField) {
this.refs[nextField].focus()
},
render: function() {
return (
<View style={styles.container}>
<TextInput
ref='1'
style={styles.input}
placeholder='Normal'
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('2')}
/>
<TextInput
ref='2'
style={styles.input}
keyboardType='email-address'
placeholder='Email Address'
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('3')}
/>
<TextInput
ref='3'
style={styles.input}
keyboardType='url'
placeholder='URL'
returnKeyType='next'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('4')}
/>
<TextInput
ref='4'
style={styles.input}
keyboardType='numeric'
placeholder='Numeric'
blurOnSubmit={false}
onSubmitEditing={() => this._focusNextField('5')}
/>
<TextInput
ref='5'
style={styles.input}
keyboardType='numbers-and-punctuation'
placeholder='Numbers & Punctuation'
returnKeyType='done'
/>
</View>
);
}
});
Using callback refs instead of the legacy string refs:
<TextInput
style = {styles.titleInput}
returnKeyType = {"next"}
autoFocus = {true}
placeholder = "Title"
onSubmitEditing={() => {this.nextInput.focus()}}
/>
<TextInput
style = {styles.descriptionInput}
multiline = {true}
maxLength = {200}
placeholder = "Description"
ref={nextInput => this.nextInput = nextInput}
/>
For the accepted solution to work if your TextInput
is inside another component, you'll need to "pop" the reference from ref
to the parent container.
// MyComponent
render() {
<View>
<TextInput ref={(r) => this.props.onRef(r)} { ...this.props }/>
</View>
}
// MyView
render() {
<MyComponent onSubmitEditing={(evt) => this.myField2.focus()}/>
<MyComponent onRef={(r) => this.myField2 = r}/>
}
This is the way I achieved it. And the example below has used the React.createRef() API introduced in React 16.3.
class Test extends React.Component {
constructor(props) {
super(props);
this.secondTextInputRef = React.createRef();
}
render() {
return(
<View>
<TextInput
placeholder = "FirstTextInput"
returnKeyType="next"
onSubmitEditing={() => { this.secondTextInputRef.current.focus(); }}
/>
<TextInput
ref={this.secondTextInputRef}
placeholder = "secondTextInput"
/>
</View>
);
}
}
I think this will help you.
There is a way to capture tabs in a TextInput
. It's hacky, but better than nothing.
Define an onChangeText
handler that compares the new input value with the old, checking for a \t
. If one is found, advance the field as shown by @boredgames
Assuming the variable username
contains the value for the username and setUsername
dispatches an action to change it in the store (component state, redux store, etc), do something like this:
function tabGuard (newValue, oldValue, callback, nextCallback) {
if (newValue.indexOf('\t') >= 0 && oldValue.indexOf('\t') === -1) {
callback(oldValue)
nextCallback()
} else {
callback(newValue)
}
}
class LoginScene {
focusNextField = (nextField) => {
this.refs[nextField].focus()
}
focusOnPassword = () => {
this.focusNextField('password')
}
handleUsernameChange = (newValue) => {
const { username } = this.props // or from wherever
const { setUsername } = this.props.actions // or from wherever
tabGuard(newValue, username, setUsername, this.focusOnPassword)
}
render () {
const { username } = this.props
return (
<TextInput ref='username'
placeholder='Username'
autoCapitalize='none'
autoCorrect={false}
autoFocus
keyboardType='email-address'
onChangeText={handleUsernameChange}
blurOnSubmit={false}
onSubmitEditing={focusOnPassword}
value={username} />
)
}
}
in your component:
constructor(props) {
super(props);
this.focusNextField = this
.focusNextField
.bind(this);
// to store our input refs
this.inputs = {};
}
focusNextField(id) {
console.log("focus next input: " + id);
this
.inputs[id]
._root
.focus();
}
Note: I used ._root
because it is a ref to TextInput in NativeBase'Library' Input
and in your text inputs like this
<TextInput
onSubmitEditing={() => {
this.focusNextField('two');
}}
returnKeyType="next"
blurOnSubmit={false}/>
<TextInput
ref={input => {
this.inputs['two'] = input;
}}/>
My scenario is < CustomBoladonesTextInput /> wrapping a RN < TextInput />.
I solved this issue as follow:
My form looks like:
<CustomBoladonesTextInput
onSubmitEditing={() => this.customInput2.refs.innerTextInput2.focus()}
returnKeyType="next"
... />
<CustomBoladonesTextInput
ref={ref => this.customInput2 = ref}
refInner="innerTextInput2"
... />
On CustomBoladonesTextInput's component definition, I pass the refField to the inner ref prop like this:
export default class CustomBoladonesTextInput extends React.Component {
render() {
return (< TextInput ref={this.props.refInner} ... />);
}
}
And voila. Everything get back works again. Hope this helps
Here a reagent solution for a input component that has a :focus property.
The field will be focused as long as this prop is set to true and will not have focus as long as this is false.
Unfortunately this component needs to have a :ref defined, I could not find an other way to call .focus() on it. I am happy about suggestions.
(defn focusable-input [init-attrs]
(r/create-class
{:display-name "focusable-input"
:component-will-receive-props
(fn [this new-argv]
(let [ref-c (aget this "refs" (:ref init-attrs))
focus (:focus (ru/extract-props new-argv))
is-focused (.isFocused ref-c)]
(if focus
(when-not is-focused (.focus ref-c))
(when is-focused (.blur ref-c)))))
:reagent-render
(fn [attrs]
(let [init-focus (:focus init-attrs)
auto-focus (or (:auto-focus attrs) init-focus)
attrs (assoc attrs :auto-focus auto-focus)]
[input attrs]))}))
https://gist.github.com/Knotschi/6f97efe89681ac149113ddec4c396cc5
Comments
Post a Comment