DEV Community

Mikayel Ohanjanyan
Mikayel Ohanjanyan

Posted on

ReactNative, Autofocus Next TextInput

The Problem

By default ReactNative doesn't support focusing next <TextInput/> on submit.

Hack

Inspired by this StackOverflow answer

Use a Context named FormContext that will create a LinkedList out of mounted <TextInput/>.
Wrap a <TextInput/> in a component, which will find and focus next <TextInput/> from FormContext.

Here is how the FormContext and FormContextProvider will look like.

// form.context.js

export const FormContext = createContext({});

let inputsLL = null;
let lastInput = null;

const FormContextProvider = ({ children }) => {

  useEffect(() => {
    inputsLL = null;
    lastInput = null;
  }, []);

  return (
    <FormContext.Provider
      value={{
        actions: {
          addInput(name, ref) {
            ref.name = name;
            if (!inputsLL) {
              inputsLL = ref;
              lastInput = ref;
            } else if (lastInput) {
              lastInput.next = ref;
              lastInput = ref;
            }
          },
          removeInput(name) {
            if (inputsLL) {
              let node = inputsLL;
              let parent = null;
              do {
                if (node.name === name) {
                  if (parent && node.next) {
                    parent.next = node.next;
                  } else if (parent && !node.next) {
                    parent.next = null;
                  } else if (!parent && node.next) {
                    inputsLL = node.next;
                  } else if (!parent && !node.next) {
                    inputsLL = null;
                  }
                  break;
                }
                parent = node;
                node = node.next;
              } while (node);
            }
          },
          focusNext(name) {
            if (inputsLL) {
              let node = inputsLL;
              do {
                if (node.name === name) {
                  if (node.next) {
                    node.next.focus();
                  } else node.blur();
                }
                node = node.next;
              } while (node);
            }
          },
        },
        inputs: inputsLL,
      }}
    >
      {children}
    </FormContext.Provider>
  );
};

export default FormContextProvider;
Enter fullscreen mode Exit fullscreen mode

Here is a <TextInput/> wrapper implementation

// wrapped-input.js

export default function WrappedInput(props) {
 let reference = useRef(null);

  const { actions } = useContext(FormContext);

  useEffect(() => {
    //label must be unique
    actions.addInput(props.label, reference);

    return () => {
      actions.removeInput(props.label);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [reference]);

  return (
    <TextInput
      ref={(r) => (reference = r)}
      blurOnSubmit={false}
      onSubmitEditing={() => {
        actions.focusNext(props.label);
      }}
    />
  )
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)