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
refattribute to the inputs - Create a function to handle
onChangefrom 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 for this. Have you tried with custom wrapper functional components?
Thanks, This is very useful. After, 3 hours.