329

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!

2
  • Mitch's answer (currently the 3rd one down) works for me on v0.42.
    – Lawrence
    Commented Mar 9, 2017 at 21:21
  • 2
    For people on React v16.8.0 or above I'd recommend the answer provided by @Eli Johnson towards the bottom. React has deprecated many uses of ref provided in solutions below.
    – thedeg123
    Commented Mar 31, 2020 at 1:56

25 Answers 25

472

Set the second TextInput focus, when the previous TextInput's onSubmitEditing is triggered.

Try this

  1. Adding a Ref to second TextInput
    ref={(input) => { this.secondTextInput = input; }}

  2. Bind focus function to first TextInput's onSubmitEditing event.
    onSubmitEditing={() => { this.secondTextInput.focus(); }}

  3. 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"
/>
21
  • 79
    Worth to mention, that onSubmitEditing callback is called after blur event. So the keyboard may go crazy if focused on next element immediately. So it might be helpful to set blurOnSubmit={false} to all elements in form but leave at true on last element, to allow Done button to blur the last input.
    – e1dar
    Commented Aug 1, 2016 at 17:37
  • 11
    This doesn't work anymore, as of v0.36. There is no method "focus" on the component. How should we do this now?
    – Mitch
    Commented Nov 1, 2016 at 5:56
  • 4
    @Mitch works fine on 0.40.0. Might have been a bug in the version you were running.
    – vikki
    Commented Feb 6, 2017 at 6:24
  • 4
    Using RN 0.49, adding blurOnSubmit={false} to prevent keyboard flickering caused this to stop working, any one that knows what's going on? Commented Nov 9, 2017 at 0:12
  • 20
    For anyone who couldn't manage to make focus work, make sure you don't use a wrapper for TextInput component. If you have a say CustomTextInput component that wraps TextInput, you need to implement TextInput blur and focus methods for that component so it will work as expected. Commented Jan 27, 2018 at 0:18
133

Thought I would share my solution using a function component... 'this' not needed!

React 16.12.0 and React Native 0.61.5

Here is an example of my component:

import React, { useRef } from 'react'
...


const MyFormComponent = () => {

  const ref_input2 = useRef();
  const ref_input3 = useRef();

  return (
    <>
      <TextInput
        placeholder="Input1"
        autoFocus={true}
        returnKeyType="next"
        onSubmitEditing={() => ref_input2.current.focus()}
      />
      <TextInput
        placeholder="Input2"
        returnKeyType="next"
        onSubmitEditing={() => ref_input3.current.focus()}
        ref={ref_input2}
      />
      <TextInput
        placeholder="Input3"
        ref={ref_input3}
      />
    </>
  )
}
13
  • 10
    not working. undefined is not object evaluating _this2.ref_input2.current,please help
    – Deep Adhia
    Commented Feb 20, 2020 at 6:24
  • 5
    For those who like keeping up with the latest react updates, this is THE ANSWER.
    – Ren
    Commented Apr 21, 2020 at 18:31
  • 14
    Nice solution. I recommend including blurOnSubmit={false} on the first two inputs to prevent keyboard flicker
    – grant
    Commented Aug 26, 2020 at 18:15
  • 13
    For those that are using a wrapper component for Text Input, I found that I needed to pass down Ref using React.forwardRef() and then pass ref as prop to Text Input
    – Bader
    Commented Oct 7, 2020 at 17:30
  • 6
    for anyone using typescript and getting Object is possibly 'null'. change these two lines: const ref_input2 = useRef<TextInput | null>(null); and onSubmitEditing={() => ref_input2.current?.focus()} Commented Oct 5, 2022 at 17:15
77

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.

  1. Add a state variable that we'll pass as a prop to the DescriptionInput:

    initialState() {
      return {
        focusDescriptionInput: false,
      };
    }
    
  2. Define a handler method that will set this state variable to true:

    handleTitleInputSubmit() {
      this.setState(focusDescriptionInput: true);
    }
    
  3. Upon submitting / hitting enter / next on the TitleInput, we'll call handleTitleInputSubmit. This will set focusDescriptionInput to true.

    <TextInput 
       style = {styles.titleInput}
       returnKeyType = {"next"}
       autoFocus = {true}
       placeholder = "Title" 
       onSubmitEditing={this.handleTitleInputSubmit}
    />
    
  4. DescriptionInput's focus prop is set to our focusDescriptionInput state variable. So, when focusDescriptionInput changes (in step 3), DescriptionInput will re-render with focus={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(); 
    }
16
  • 2
    @LaneRettig You're totally right -- thanks for pointing that out. We wrap RN TextInput with some added props and methods -- please see the bottom of the answer w/ those additions and let me know if you have any further issues! Commented Feb 28, 2016 at 23:19
  • 4
    Cool. You should submit this as a PR to RN. I'm surprised this isn't already supported out of the box. Commented Feb 28, 2016 at 23:33
  • 11
    what if you click next on keyboard, and then click directly on first input? focus goes back to second which is bad experience with that solution
    – Piotr
    Commented Apr 8, 2016 at 14:21
  • 5
    I don't love this solution, in particular because it doesn't scale well for even slightly longer forms of 5-6 elements, where you'd need a focus boolean in state for each element & manage them all accordingly.
    – davidgoli
    Commented Mar 20, 2018 at 21:30
  • 25
    Interestingly, the docs also state: "There are a few good use cases for refs: Managing focus, text selection, or media playback..." So in this case, using refs for focusing text input would be a valid use of the tool.
    – Noah Allen
    Commented Jun 19, 2018 at 3:33
28

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" />
...
10
  • 1
    Works awesome but if anyone uses redux-form should do something like this. <Field ... onSubmitEditing={() => focusTextInput(this._password)} /> and ref should be like this <Field ... withRef refName={e => this._password = e}/>
    – tarkanlar
    Commented Nov 28, 2016 at 22:18
  • 1
    I had to use 'onSubmitEditing' to make this work but great solution none the less. Commented Dec 21, 2016 at 14:55
  • 3
    Works great in 0.42.
    – Lawrence
    Commented Mar 9, 2017 at 21:20
  • 1
    @tarkanlar can you share code snippet for the solution? I can't seem to focus when using redux-form Field, using just TextInput works, fine
    – jasan
    Commented Jun 16, 2017 at 8:14
  • 4
    calling focus() on a text input node isn't supported any more => bold claim, source? Calling focus() works fine with v0.49.5 + TextInputState is not documented while focus() and blur() are mentioned: facebook.github.io/react-native/releases/next/docs/…
    – tanguy_k
    Commented Oct 31, 2017 at 0:27
24

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/

8
  • Excellent pattern for achieving this result. Should be top answer from ease of use stand point. Looks like I can easily edit my custom FormInput (TextInput extensions) to still work with your Form inputs. Mind if I include it to your answer if for further example? Commented Aug 21, 2017 at 22:42
  • Sure! I know... I posted this on the other popular post about this but got in trouble for duplicates. Just trying to help because I know how annoying this problem is!!
    – zackify
    Commented Aug 22, 2017 at 3:02
  • 1
    This is great if you have a bunch of TextInputs right after each other but if you want to add styling between them it breaks down. Thanks for the contribution though.
    – GenericJam
    Commented Oct 23, 2017 at 2:26
  • Feel free to adjust the code. I’m sure you could make a way that would skip over elements that are not text inputs. Shouldn’t be too hard to do.
    – zackify
    Commented Oct 23, 2017 at 4:45
  • 1
    This does not build for production [email protected] Commented Dec 28, 2017 at 10:39
16

Combining @Eli Johnson's solution for functional components with @Rodrigo Tessarollo's solution for a CustomTextInput:

import React, { useRef } from 'react';
import { CustomTextInput } from 'path/to/CustomTextInput';
...


export const MyFormComponent = () => {

  const ref_to_input2 = useRef();

  return (
    <>
      <CustomTextInput
        placeholder="Input 1"
        autoFocus={true}
        returnKeyType="next"
        onSubmitEditing={() => ref_to_input2.current.focus()}
      />
      <CustomTextInput
        placeholder="Input 2"
        returnKeyType="done"
        refInner={ref_to_input2}
        onSubmitEditing={/* Do something! */}
      />
    </>
  )
}

and in your CustomTextInput component:

import { TextInput } from "react-native";
export const CustomTextInput = (props) => {
  <TextInput
        ref={props.refInner}
        {...props}
  />
}
1
  • Have you tried to have a single Ref for all inputs? Your solution worked, but in big forms it is quite verbose to have a single ref for each input.
    – MorenoMdz
    Commented Jun 18, 2021 at 19:12
15

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>
        )
    }
}
2
  • You save my life : ) Commented Oct 11, 2018 at 8:49
  • 2
    You've merely renamed ref to inputRef... You could drop your entire custom component and your second code block will work as-is as long as you revert back to using ref Commented May 29, 2019 at 2:47
12

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()

1
  • 1
    This is 'native-base' specific
    – Developia
    Commented Aug 30, 2018 at 7:44
9

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

0
8

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)

1
  • how do I call function onSubmitEditing ? for ex: I want to call login() function when user press last textinput's returnkeytype 'done'.
    – chetan
    Commented Dec 14, 2017 at 11:22
8

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.

1
  • what is the purpose of .current?
    – Adam Katz
    Commented Dec 19, 2019 at 11:09
6

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>
        );
    }
});
4
  • Please include the relative information from the link in your answer.
    – Wes Foster
    Commented Sep 24, 2015 at 4:27
  • 1
    Keep in mind that string refs may become deprecated so this solution may not work in the future: "...Although string refs are not deprecated, they are considered legacy, and will likely be deprecated at some point in the future. Callback refs are preferred." -- facebook.github.io/react/docs/more-about-refs.html
    – yura
    Commented Aug 9, 2016 at 21:05
  • 1
    This doesn't work anymore, as of v0.36. There is no method "focus" on the component. How should we do this now? Can you update the answer?
    – Mitch
    Commented Nov 1, 2016 at 5:57
  • @Mitch not sure if this is back on 0.39.2 but this works fine now.
    – Eldelshell
    Commented Dec 17, 2016 at 18:41
5
<TextInput placeholder="Nombre"
    ref="1"
    editable={true}
    returnKeyType="next"
    underlineColorAndroid={'#4DB6AC'}
    blurOnSubmit={false}
    value={this.state.First_Name}
    onChangeText={First_Name => this.setState({ First_Name })}
    onSubmitEditing={() => this.focusNextField('2')}
    placeholderTextColor="#797a7a" style={{ marginBottom: 10, color: '#808080', fontSize: 15, width: '100%', }} />

<TextInput placeholder="Apellido"
    ref="2"
    editable={true}
    returnKeyType="next"
    underlineColorAndroid={'#4DB6AC'}
    blurOnSubmit={false}
    value={this.state.Last_Name}
    onChangeText={Last_Name => this.setState({ Last_Name })}
    onSubmitEditing={() => this.focusNextField('3')}
    placeholderTextColor="#797a7a" style={{ marginBottom: 10, color: '#808080', fontSize: 15, width: '100%', }} />

and add method

focusNextField(nextField) {
    this.refs[nextField].focus();
}
1
  • 2
    Old answer but does anyone know if it's possible to access all refs like in this answer in a functional (stateless) component? Commented Dec 4, 2019 at 19:30
4

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}
/>
1
  • 2
    Doesn't work as the focus-method has been removed from TextInput. Commented Mar 16, 2017 at 8:51
3

Here is how achieved this for reactjs phone code inputs

import React, { useState, useRef } from 'react';

function Header(props) {

  const [state , setState] = useState({
        phone_number:"",
        code_one:'',
        code_two:'',
        code_three:'',
        code_four:'',
        submitted:false,

  })

   const codeOneInput = useRef(null);
   const codeTwoInput = useRef(null);
   const codeThreeInput = useRef(null);
   const codeFourInput = useRef(null);

   const handleCodeChange = (e) => {
        const {id , value} = e.target
        if(value.length < 2){
            setState(prevState => ({
                ...prevState,
                [id] : value
            }))
            if(id=='code_one' && value.length >0){
                codeTwoInput.current.focus();
            }
            if(id=='code_two'  && value.length >0){
                codeThreeInput.current.focus();
            }
            if(id=='code_three'  && value.length >0){
                codeFourInput.current.focus();
            }
        }
    }

    const sendCodeToServer = () => {

         setState(prevState => ({
                ...prevState,
                submitted : true,
          }))
  let codeEnteredByUser = state.code_one + state.code_two + state.code_three + state.code_four

        axios.post(API_BASE_URL, {code:codeEnteredByUser})
        .then(function (response) {
            console.log(response)
        })

   }

   return(
        <>

           <div className="are">
                 <div className="POP-INN-INPUT">
                                        <input type="text" id="code_one" ref={codeOneInput}    value={state.code_one}  onChange={handleCodeChange} autoFocus/>
                                        <input type="text" id="code_two"  ref={codeTwoInput}  value={state.code_two} onChange={handleCodeChange}/>
                                        <input type="text" id="code_three"  ref={codeThreeInput} value={state.code_three}  onChange={handleCodeChange}/>
                                        <input type="text" id="code_four" ref={codeFourInput}  value={state.code_four}  onChange={handleCodeChange}/>
                                    </div>

            <button disabled={state.submitted} onClick={sendCodeToServer}>
   
    </div>

       </>
    )
}
export default

enter image description here

2

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}/>
}
3
  • 1
    Hi @Eldelshell, I would like to achieve the same thing, but could not sort out your sample, would you mind show us a hint?
    – Seeliang
    Commented Jun 12, 2017 at 11:29
  • I think this should be the correct answer. I follow this and it works.
    – chubao
    Commented Feb 1, 2018 at 9:06
  • are these both in the same file?
    – MoralCode
    Commented Jul 16, 2018 at 0:16
2

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;
                        }}/>
2
<TextInput 
    keyboardType="email-address"
    placeholder="Email"
    returnKeyType="next"
    ref="email"
    onSubmitEditing={() => this.focusTextInput(this.refs.password)}
    blurOnSubmit={false}
 />
<TextInput
    ref="password"
    placeholder="Password" 
    secureTextEntry={true} />

And add method for onSubmitEditing={() => this.focusTextInput(this.refs.password)} as below:

private focusTextInput(node: any) {
    node.focus();
}
1

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} />
    )
  }
}
1
  • This didn't work for me using a physical keyboard. the onChangeText event doesn't fire on tab.
    – Bufke
    Commented Dec 21, 2016 at 22:35
1

Really annoying that RN doesn't have some sort of Tabindex system.

A functional component, for my use case, I have an array of string IDs for inputs which I iterate through and show one text input each. The following code will automatically jump the user through all of them, stopping the keyboard from disappearing/reappearing between fields and dismissing it at the end, also showing the appropriate "action" button on the keyboard.

Typescript, Native Base.

const stringFieldIDs = [
  'q1', 'q2', 'q3'
];

export default () => {
  const stringFieldRefs = stringFieldIDs.map(() => useRef < any > ());

  const basicStringField = (id: string, ind: number) => {
    const posInd = stringFieldIDs.indexOf(id);
    const isLast = posInd === stringFieldIDs.length - 1;

    return ( <
      Input blurOnSubmit = {
        isLast
      }
      ref = {
        stringFieldRefs[posInd]
      }
      returnKeyType = {
        isLast ? 'done' : 'next'
      }
      onSubmitEditing = {
        isLast ?
        undefined :
          () => stringFieldRefs[posInd + 1].current._root.focus()
      }
      />
    );
  };

  return stringFieldIDs.map(basicStringField);
};

1
import React, { useState, useEffect, useRef, } from 'react';

const OTP = (props) => {



    const OTP = [];
    const ref_input = [];
    ref_input[0] = useRef();
    ref_input[1] = useRef();
    ref_input[2] = useRef();
    ref_input[3] = useRef();

    const focusNext = (text, index) => {
        if (index < ref_input.length - 1 && text) {
            ref_input[index + 1].current.focus();
        }
        if (index == ref_input.length - 1) {
            ref_input[index].current.blur();
        }
        OTP[index] = text;
    }
    const focusPrev = (key, index) => {
        if (key === "Backspace" && index !== 0) {
            ref_input[index - 1].current.focus();
        }
    }

    return (
        <SafeAreaView>
            <View>
                
                    <ScrollView contentInsetAdjustmentBehavior="automatic" showsVerticalScrollIndicator={false}>
                        <View style={loginScreenStyle.titleWrap}>
                            <Title style={loginScreenStyle.titleHeading}>Verify OTP</Title>
                            <Subheading style={loginScreenStyle.subTitle}>Enter the 4 digit code sent to your mobile number</Subheading>
                        </View>
                        <View style={loginScreenStyle.inputContainer}>
                            <TextInput
                                mode="flat"
                                selectionColor={Colors.primaryColor}
                                underlineColorAndroid="transparent"
                                textAlign='center'
                                maxLength={1}
                                keyboardType='numeric'
                                style={formScreenStyle.otpInputStyle}
                                autoFocus={true}
                                returnKeyType="next"
                                ref={ref_input[0]}
                                onChangeText={text => focusNext(text, 0)}
                                onKeyPress={e => focusPrev(e.nativeEvent.key, 0)}
                            />
                            <TextInput
                                mode="flat"
                                selectionColor={Colors.primaryColor}
                                underlineColorAndroid="transparent"
                                textAlign='center'
                                maxLength={1}
                                keyboardType='numeric'
                                style={formScreenStyle.otpInputStyle}
                                ref={ref_input[1]}
                                onChangeText={text => focusNext(text, 1)}
                                onKeyPress={e => focusPrev(e.nativeEvent.key, 1)}
                            />
                            <TextInput
                                mode="flat"
                                selectionColor={Colors.primaryColor}
                                underlineColorAndroid="transparent"
                                textAlign='center'
                                maxLength={1}
                                keyboardType='numeric'
                                style={formScreenStyle.otpInputStyle}
                                ref={ref_input[2]}
                                onChangeText={text => focusNext(text, 2)}
                                onKeyPress={e => focusPrev(e.nativeEvent.key, 2)}
                            />
                            <TextInput
                                mode="flat"
                                selectionColor={Colors.primaryColor}
                                underlineColorAndroid="transparent"
                                textAlign='center'
                                maxLength={1}
                                keyboardType='numeric'
                                style={formScreenStyle.otpInputStyle}
                                ref={ref_input[3]}
                                onChangeText={text => focusNext(text, 3)}
                                onKeyPress={e => focusPrev(e.nativeEvent.key, 3)}
                            />

                        </View>
                    </ScrollView>
            </View>
        </SafeAreaView >
    )
}

export default OTP;
0

If you are using NativeBase as UI Components you can use this sample

<Item floatingLabel>
    <Label>Title</Label>
    <Input
        returnKeyType = {"next"}
        autoFocus = {true}
        onSubmitEditing={(event) => {
            this._inputDesc._root.focus(); 
        }} />
</Item>
<Item floatingLabel>
    <Label>Description</Label>
    <Input
        getRef={(c) => this._inputDesc = c}
        multiline={true} style={{height: 100}} />
        onSubmitEditing={(event) => { this._inputLink._root.focus(); }} />
</Item>
0

onSubmitEditing is called when returnKeyType key is pressed. when you switch to next input, to prevent the flashing (in a fast system can be ignored) blurOnSubmit={false}

import React, { ReactElement, useRef, useState } from "react";
import {  ScrollView, TextInput as NativeTextInput, TouchableOpacity } from "react-native";

    
export default function LoginScreen({ navigation, route }: LoginProps): ReactElement {
    const passwordRef = useRef<NativeTextInput | null>(null);
    const [form, setForm] = useState({
        username: "player1",
        password: "player1234"
    });
    const setFormInput = (key: keyof typeof form, value: string) =>
        setForm({ ...form, [key]: value });

    return (
            <ScrollView contentContainerStyle={styles.container}>
                <TextInput
                    value={form.username}
                    onChangeText={value => setFormInput("username", value)}
                    returnKeyType="next"
                    blurOnSubmit={false}
                    placeholder="Username"
                    onSubmitEditing={() => {
                        passwordRef.current?.focus();
                    }}
                />
                <TextInput
                    value={form.password}
                    onChangeText={value => setFormInput("password", value)}
                    ref={passwordRef}
                    returnKeyType="done"
                    secureTextEntry
                    placeholder="Password"
                />
            </ScrollView>
    );
}
-1

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

1
  • @Bap - this is Clojurescript. Reagent is a binding to React. If you're curious, it's a great match for React if you are up for a lisp as stateful updates are generally only possible with things like an explicit call to swap! on an atom type. As per the docs, this is used for binding to React: "Any component that uses an atom is automagically re-rendered when its value changes." reagent-project.github.io
    – Del
    Commented Apr 1, 2019 at 17:29
-1

If you've wrapped a React Native TextInput within a new component and you find that the ref is not working as expected to move to the next input when submitted, there are a few potential reasons and solutions you can explore:

  1. Passing the ref properly:

Ensure that you are correctly passing the ref from the parent component to the wrapped TextInput component. If you are using functional components, you can use the useImperativeHandle hook to expose specific methods or properties of the child component.

  1. Using forwardRef:

Ensure that you are using the forwardRef function when creating your custom component. This is necessary to forward the ref from the parent component to the child component.

  1. Handling focus in the parent component:

If the ref is properly passed, ensure that you are using the focus() method on the next TextInput component in the parent component's handleSubmit function.

//ParentComponent.js
import React, { useRef } from 'react';
import CustomInput from './CustomInput';

const ParentComponent = () => {
   const inputRef1 = useRef(null);
   const inputRef2 = useRef(null);

  const handleSubmit = () => {
    // Do something with the input value
    inputRef2.current.focus();
  };

  return (
    <CustomInput ref={inputRef1} onSubmit={handleSubmit} />
  );
};

// CustomInput.js
import React, { forwardRef } from 'react';
import { TextInput, Button } from 'react-native';

const CustomInput = forwardRef(({ onSubmit }, ref) => {
  return (
    <>
      <TextInput ref={ref} /* other props */ />
      <Button title="Submit" onPress={onSubmit} />
    </>
  );
});

export default CustomInput;

worked for me after trying several solutions

2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.