TypeScript下React Native使用useRef实现自定义TextInput聚焦下一个输入框
问题原因
你当前的实现错误点在于将输入框ref定义在了GeneralTextInput组件内部,每个GeneralTextInput实例的ref都是独立存储的,第一个输入框实例内部的groupDescRef根本无法指向第二个输入框的真实实例,所以调用focus方法无效。
修改步骤
1. 改造GeneralTextInput.tsx支持透传ref
首先使用React.forwardRef包裹自定义输入组件,将父组件传入的ref绑定到内部原生TextInput上,同时补充onSubmitEditing到props接口允许父组件自定义提交逻辑:
// 首先给IGeneralTextInputProps新增可选的onSubmitEditing属性 export interface IGeneralTextInputProps extends IStyledComponent<GeneralTextInputStyles> { // 原有所有属性保持不变,新增以下属性 readonly onSubmitEditing?: () => void; } // 用forwardRef改造组件,泛型第一个参数是ref指向的原生TextInput类型,第二个是props类型 export const GeneralTextInput = React.forwardRef<TextInput, IGeneralTextInputProps>((props, ref) => { const classes = useStyles(props); const { fonts, colors } = useTheme(); const [isPressed, setIsPressed] = React.useState(false); const [isPasswordVisible, setPasswordVisible] = React.useState(false); // 删掉原来组件内部定义的groupNameRef、groupDescRef,这两个ref跨实例无法访问没有作用 return ( <View style={classes.container}> <TouchableWithoutFeedback> <View style={classes.root}> <TextInput // 绑定父组件传入的ref ref={ref} // 优先执行父组件传入的提交逻辑 onSubmitEditing={props.onSubmitEditing} 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> ); });
2. 修改父组件代码控制跳转逻辑
父组件内创建两个ref分别对应两个输入框,把ref传入对应的GeneralTextInput,同时给第一个输入框配置提交时聚焦第二个输入框的逻辑:
// 父组件顶部先创建对应ref const groupNameRef = React.useRef<TextInput>(null); const groupDescRef = React.useRef<TextInput>(null); // 渲染部分修改如下 <View style={classes.formContainer}> <Text style={classes.label}>{t("group.name-your-group")}</Text> <GeneralTextInput ref={groupNameRef} width={"100%"} returnKeyType={"next"} isDoneReference={false} deleteIcon startIcon={"account-multiple"} bordered={true} placeholder={t("form.placeholders.groupName")} value={props.newGroupName} // 新增提交逻辑:点击键盘下一步聚焦描述输入框 onSubmitEditing={() => groupDescRef.current?.focus()} 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 ref={groupDescRef} 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>
额外注意事项
- 如果你使用的是React Native 0.65+版本,多行输入框设置
returnKeyType为next/done时,需要额外设置blurOnSubmit={false}才会触发onSubmitEditing而不是直接换行,你当前第二个输入框是done类型,设置了blurOnSubmit={true}符合需求。 - 不要在通用复用组件内部存储特定业务的ref,所有跨组件的ref访问都应该放在父业务层维护,保证复用组件的通用性。
内容的提问来源于stack exchange,提问作者Oğuzhan Yıldırım
相关产品推荐
相关产品推荐

