/ react native

Autofocus inputs in React native

Having a stack of TextInput's that autofocus on the next one when hitting enter is a manual process in React Native. I'm going to show you how to solve this problem once and for all. The final product will look like this:

import { Form, TextInput } from 'react-native-autofocus'

export default () => (
  <Form>
    <TextInput placeholder="test" />
    <TextInput placeholder="test 2" />
  </Form>
)

You can install the package now and check it out on github.

We need to create a component that is wrapped around all form inputs. After that, we need to add a really tiny wrapper around the native text input fields. After doing so, we will never mess with manually focusing on the next field ever again!

Form

import React from 'react';
import { View } from 'react-native';

export default class Form extends React.Component {
  constructor() {
    super();
    this.inputs = [];
  }

  renderChildren(children) {
    return React.Children.map(children, (child, index) => {
      if (child.children) return this.renderChildren(children);
      if (child.type.name !== 'TextInput') return child;

      return React.cloneElement(child, {
        onEnter: () =>
          this.inputs[index + 1] ? this.inputs[index + 1].focus() : null,
        inputRef: ref => (this.inputs[index] = ref),
      });
    });
  }

  render() {
    let { children, ...props } = this.props;
    return (
      <View {...props}>
        {this.renderChildren(children)}
      </View>
    );
  }
}

In the Form component, we are looping over every child element, checking if it is a TextInput, and adding two props if it is. We are passing along an onEnter function that will be called if there is another input after this one. We then add the component's reference to the Form's instance. This way it can call focus on the next input, if it exists. Please note that mixing in other components inbetween your inputs will mess up the onEnter, so only wrap your input fields, or send me a PR :)

TextInput

import React from 'react';
import { TextInput as Input } from 'react-native';

const TextInput = ({ onSubmitEditing, onEnter, inputRef, ...props }) => (
  <Input
    ref={ref => inputRef(ref)}
    onSubmitEditing={() => {
      if (onEnter) onEnter();
      if (onSubmitEditing) onSubmitEditing();
    }}
    {...props}
  />
);

export default TextInput;

All we do in our wrapped TextInput is check for an onEnter prop, call it on submit if it exists, and check for a real onSubmitEditing prop in case the end user adds that prop as well. Second, we pass along the ref to the Form's inputRef prop. That's really all there is to it! If you copy / paste this code, or npm install react-native-autofocus into a fresh app, it'll just work! Nothing is different other than hitting enter actually goes into the next field.

If you want to accomplish something close to this, or support more fields, send a pull request to this repo. I decided while making this post it was worth open sourcing, even though this is such a small problem in the grand scheme of things. Hit me on on Twitter with your thoughts.