Skip to content

React Native Text Input focus with useRef using Typescript

I have an issue with focusing the next input in React Native. I use just one input called GeneralTextInput.tsx in the whole app.

In this example I have 2 inputs ==> 1.Group Name, 2.Group Description

So I give some props in the parent to this component:

<View style={classes.formContainer}>
  <Text style={classes.label}>{t("group.name-your-group")}</Text>

  <GeneralTextInput
    width={"100%"}
    returnKeyType={"next"}
    isDoneReference={false}
    deleteIcon
    startIcon={"account-multiple"}
    bordered={true}
    placeholder={t("form.placeholders.groupName")}
    value={props.newGroupName}
    onChange={(val: string) => {
      props.setNewGroupName(val);
      if (val.length > 25) {
        props.setNewGroupNameError(t("form.validations.max-25-char"));
      }
      if (val.length <= 25) {
        props.setNewGroupNameError(undefined);
      }
    }}
  />

  <Text style={classes.label}>{t("group.describe-your-group")}</Text>

  <GeneralTextInput
    width={"100%"}
    returnKeyType={"done"}
    isDoneReference={true}
    isDismissed={true}
    startIcon={"text"}
    bordered={true}
    isMultiLine={true}
    numberOfLines={3}
    placeholder={t("form.placeholders.groupDescription")}
    value={props.newGroupDescription}
    onChange={(val: string) => {
      props.setNewGroupDescription(val);
      if (val.length > 30) {
        props.setNewGroupDescriptionError(t("form.validations.max-30-char"));
      }
      if (val.length < 30) {
        props.setNewGroupDescriptionError(undefined);
      }
    }}
  />
</View>

And this is my GeneralTextInput.tsx What should I give to the input as a ref and how should I focus on it?

import * as React from "react";
import {
    NativeSyntheticEvent,
    Platform,
    StyleProp,
    TextInputFocusEventData,
    TextStyle,
    View,
    ViewStyle,
    TextInput,
    ImageStyle,
    Pressable,
} from "react-native";
import { makeStyles, IStyledComponent } from "../../assets/theme/installation";
import { IconButton, Text, useTheme } from "react-native-paper";
import Icon from "react-native-vector-icons/MaterialCommunityIcons";
import FontAwesome5Icon from "react-native-vector-icons/FontAwesome5";
import { theme } from "../../assets/theme/DefaultTheme";
import { TouchableWithoutFeedback } from "react-native-gesture-handler";

export interface IGeneralTextInputProps
    extends IStyledComponent<GeneralTextInputStyles> {
    readonly value: string | undefined;
    readonly placeholder?: string;
    readonly onChange: (newValue: string) => void;
    readonly onBlur?: (e: NativeSyntheticEvent<TextInputFocusEventData>) => void;
    readonly isPassword?: boolean;
    readonly autoCapitalize?: boolean;
    readonly error?: string;
    readonly startIcon?: string;
    readonly startIconFA5?: string;
    readonly endIcon?: string;
    readonly deleteIcon?: boolean;
    readonly disabled?: boolean;
    readonly disabledInputText?: boolean;
    readonly bordered?: boolean;
    readonly isMultiLine?: boolean;
    readonly width?: number | string;
    readonly numberOfLines?: number;
    readonly keyboardType?: string;
    readonly isGratitude?: boolean;
    readonly autoCorrect?: boolean;
    readonly selectedMeasureUnit?: string;
    readonly returnKeyType?: string;
    readonly isDoneReference?: boolean;
    readonly isDismissed?: boolean;
}

export const GeneralTextInput: React.FC<IGeneralTextInputProps> = (
    props: IGeneralTextInputProps,
) => {
    const classes = useStyles(props);
    const { fonts, colors } = useTheme();
    const [isPressed, setIsPressed] = React.useState(false);
    const [isPasswordVisible, setPasswordVisible] = React.useState(false);

    const groupNameRef = React.useRef<HTMLInputElement>(null);
    const groupDescRef = React.useRef<HTMLInputElement>(null);

    return (
    <View style={classes.container}>
        <TouchableWithoutFeedback>
        <View style={classes.root}>
            <TextInput
            ref={() => (props.isDoneReference ? groupDescRef : groupNameRef)}
            onSubmitEditing={() => {
                groupDescRef.current?.focus();
            }}
            blurOnSubmit={props.isDoneReference ? true : false}
            keyboardType={
                props.keyboardType === "numpad" ? "numeric" : "default"
            }
            autoCorrect={props.autoCorrect}
            multiline={props.isMultiLine}
            numberOfLines={props.numberOfLines}
            maxLength={props.isGratitude ? 300 : 50}
            editable={!props.disabled}
            onBlur={props.onBlur}
            autoCapitalize={
                props.autoCapitalize != undefined ? "words" : "none"
            }
            secureTextEntry={
                props.isPassword == undefined ? false : !isPasswordVisible
            }
            style={
                props.disabledInputText
                ? classes.disabledTextInput
                : classes.textInput
            }
            value={props.value}
            placeholder={props.placeholder}
            placeholderTextColor={fonts.text.small.color}
            onTouchEnd={() => setIsPressed(true)}
            onChangeText={(value) => props.onChange(value)}
            returnKeyType={
                props.returnKeyType === "next"
                ? "next"
                : props.returnKeyType === "done"
                ? "done"
                : "default"
            }
            />
        </View>
        </TouchableWithoutFeedback>
    </View>
    );
};

Answer

You wrap GeneralTextInput with forwardRef:

import { TextInput, TextInputProps } from "react-native";


export const GeneralTextInput: React.forwardRef<TextInput,IGeneralTextInputProps> = (
  // type of props and ref will be inferred by ts
  props
  ref
) => {
     .... 
     return (
     ....
     <TextInput
        ref={ref}
        {...props}
     ...
     ...

    />
    )}

Now in the parent component Define one useRef:

const secondInputRef = useRef<TextInput | null>(null);

you have 2 generalInput. on first input

<GeneralTextInput
    ....
    ....
    // add this. this will focus on secondInput
    onSubmitEditing={() => {
                             secondInputRef.current?.focus();
                           }}
  />

second GeneralInput will be as it is