You must be familiar of using Tab key to focus to next element when browsing the web. It's a simple trick to make your browsing experience easier. Recently, I am making an app with React Native that has a one-time password (OTP) form. I am planning to give the same experience when user inserts a number the app will focus to the next TextInput
.
This can be done by using ref
attribute. Ref allows us to access the React component or DOM node that is going to be modified. Basically, what we are going to do are
- Create an array consist of
React.createRef
- Add the
ref
attribute to the inputs - Create a function to handle
onChange
from the inputs - Focus the next input found
class OtpForm extends React.Component {
constructor(props){
super(props)
this.inputRefs = [
React.createRef(),
React.createRef(),
React.createRef(),
React.createRef()
]
// or
// this.inputRefs = Array(4).fill(React.createRef())
}
}
Now above we already have an array of React.createRef
. We only need 4 digits of OTP and one digit for each inputs. So we gonna add these elements to our render
method.
...
render(){
return (
<View>
<Text>Enter OTP</Text>
<View style={{ flexDirection: 'row' }}>
<TextInput maxLength="1" />
<TextInput maxLength="1" />
<TextInput maxLength="1" />
<TextInput maxLength="1" />
</View>
</View>
)
}
...
Notice that we have not add the ref
attr to any TextInput
. To make it easier we will rewrite those code to render TextInput
based on our this.inputRefs
length, then we add the ref
attr.
...
render(){
return (
<View>
<Text>Enter OTP</Text>
<View style={{ flexDirection: 'row' }}>
{
this.inputRefs.map((k, idx) => (
<TextInput ref={r => this.inputRefs[idx] = r} maxLength="1" />
))
}
</View>
</View>
)
}
...
So basically, we iterate through our this.inputRefs
using .map
operator then we define the ref
attr for each TextInput
according to its index.
Now we already have our this.inputRefs
array elements connected to each of the TextInput
. The next thing is handling changes made by user. Since our TextInput
only going to contain 1 digit number so we only need to handle onChange
event. We are going to make a function _goNextAfterEdit
that will set focus on the next input element.
class OtpForm extends React.Component {
constructor(props){
super(props)
this.inputRefs = [
React.createRef(),
React.createRef(),
React.createRef(),
React.createRef()
]
}
// Pass the index of the current TextInput
// then plus that with +1
_goNextAfterEdit(index){
this.inputRefs[index+1].focus()
}
render(){
return (
<View>
<Text>Enter OTP</Text>
<View style={{ flexDirection: 'row' }}>
{
this.inputRefs.map((k, idx) => (
<TextInput onChange={() => this._goNextAfterEdit(idx)} ref={r => this.inputRefs[idx] = r} maxLength="1" />
))
}
</View>
</View>
)
}
}
That's it!
Basically those code satisfy our purpose. But there are additional code you can add to avoid bugs or improve functionality, such as:
Validate the last TextInput
Since we render our TextInput
based on the length of this.inputRefs
, there will be a bug when user is editing the last TextInput
provided inside the form.
...
_goNextAfterEdit(index){
if(index < this.inputRefs.length) {
this.inputRefs[index+1].focus()
}
}
...
Handling > 1 digit text input
You might not want to use onChange
event when having TextInput
that receive word or > 1 digit character. I suggest using onSubmitEditing
which fires when the user submit button from his/her keyboard.
Specify the input type
In React Native, you specify the keyboard displayed whenever the user is editing the TextInput
. You can specify it by adding the keyboardType
attr. For the complete list of keyboard type, https://facebook.github.io/react-native/docs/textinput.html#keyboardtype
Top comments (4)
hello, thanks for this. How do you do this for function components?
Hi, were you able to solve it with functional components?
Thanks, This is very useful. After, 3 hours.
Thanks for this. Have you tried with custom wrapper functional components?