React Native 中的表单,正确的方法😎
作为一名 React Native 开发者,处理 React Native 中的表单至关重要。开发一个新的 React Native 应用时,表单是必不可少的,至少在登录和注册界面,因为在大多数情况下,你的应用都需要检索数据。找到一个解决此类重复性任务的解决方案,将帮助你为下一个 Sprint 节省大量时间。
在我作为 React Native 开发者的两年经验中,我曾经尝试过各种不同的方法来处理表单,却始终找不到一个最佳方案,也不知道该如何另辟蹊径。今天,我满怀信心地想与大家分享如何在下一个 React Native 项目中正确处理表单。我非常乐意听到大家对此解决方案的评论和想法(这也是我分享这篇文章的原因)。
本文是一个分步教程,介绍如何创建一个通用表单组件,可以在您需要处理表单时使用。
如果你不耐烦,想直接看代码,可以尝试一下这个Snack Editor Project中的解决方案。😀
方法
这个想法基于react-hook-form
一个智能表单组件,它可以自动组合任何输入子项、收集数据并处理错误。此外,我们还将提供一种自动方法,在按下next
键盘按钮后自动聚焦到下一个输入,并为我们的输入添加键盘感知功能。
为了清楚起见,我们首先需要创建一个具有错误处理的自定义输入组件。然后创建一个智能表单组件,react-hook-form
为每个组件正确注入所有属性,最后实现自动对焦和键盘感知功能。
这是我们工作的最终成果。
您无法记录 secureTextEntry={true} 输入的键盘,这就是您看到我的密码的原因😀
在开始之前我想你会好奇👇
为什么选择 React-hooks-form
根据React-hook-form 官方文档,React Hook Form 的主要目标之一是减少代码编写量。从最终结果可以看出,React Hook Form 非常易于使用,并且只需少量代码。更重要的是,如果我们将其与 React 中最常用的表单处理解决方案(例如 Formik 和 redux-form)进行比较,那么在打包大小和性能方面,React Hook Formreact-hook-form
显然更胜一筹。react-hook-form
React-hook-form 主页包含一个完整的部分,说明了为什么您应该使用它而不是其他库。
让我们开始吧
创建自定义输入
在第一步中,我们将创建一个具有错误处理的自定义文本输入组件,如您所见,我们尝试使组件尽可能简单,但您可以根据需要设置组件样式,甚至可以使用内置 UI 库中的 TextInput,只要自定义输入实现 React forward API 并具有必需属性,无论您的输入实现react-paper
如何native-base
,解决方案都将按预期工作name
。
import * as React from 'react'
import {
View,
TextInput,
Text,
StyleSheet,
ViewStyle,
TextStyle,
TextInputProps,
} from 'react-native'
import { FieldError } from 'react-hook-form'
interface Props extends TextInputProps {
name: string
label?: string
labelStyle?: TextStyle
error?: FieldError | undefined
}
export default React.forwardRef<any, Props>(
(props, ref): React.ReactElement => {
const { label, labelStyle, error, ...inputProps } = props
return (
<View style={styles.container}>
{label && <Text style={[styles.label, labelStyle]}>{label}</Text>}
<TextInput
autoCapitalize="none"
ref={ref}
style={[
styles.inputContainer,
{ borderColor: error ? '#fc6d47' : '#c0cbd3' },
]}
{...inputProps}
/>
<Text style={styles.textError}>{error && error.message}</Text>
</View>
)
}
)
您可以使用InputProps
我们组件中的类型来实现您的自定义类型,并确保您的组件已准备好进行下一步。
创建表单组件
表单组件背后的理念是迭代子组件,并通过添加正确的属性来注册所有输入。
表单组件将从useForm
钩子中接收其 prop,这些属性正是我们需要传递的,以确保我们的表单组件正常工作。
- register:此方法允许我们将具有唯一名称和验证规则的输入 Ref 注册到 React Hook Form 中。
register: ({name}: {name: string}, validation: ValidationOptions) => void;
- setValue:此函数将帮助我们动态地将输入值设置为正确的 ref 使用
name
属性。我们需要使用 setValue ,因为我们将使用自定义的寄存器调用。
setValue: (name: string, value: string, shouldValidate?: boolean) => void;
- 错误:包含与每个输入对应的表单错误和错误消息的对象。
- 验证:包含每个输入的表单验证规则的对象。
如果你不熟悉 react-hook-form,请查看此处的文档
为了将 Props 注入到 Input 子项中,我们将使用React.createElement
API 为每个具有属性的子项创建新的 Element name
。
通过使用该name
属性,我们可以过滤所有需要成为表单数据一部分的输入,或者如果不是这种情况,则返回子项而不创建新的子项。
对于每个输入子项,我们使用register
函数手动注册输入引用并注入验证规则。
通过使用自定义寄存器调用,我们将需要setValue
使用onChangeText
输入属性手动更新输入值。
最后,我们将添加与每个输入相对应的错误和错误消息。
export default ({
register,
errors,
setValue,
validation,
children,
}: Props) => {
return (
<>
{(Array.isArray(children) ? [...children] : [children]).map(child => {
return child.props.name
? React.createElement(child.type, {
...{
...child.props,
ref: () => {
register(
{ name: child.props.name },
validation[child.props.name]
)
},
onChangeText: (v: string) =>
setValue(child.props.name, v, true),
key: child.props.name,
error: errors[child.props.name],
},
})
: child
})}
</>
)
}
现在,我们的表单组件已经准备好了,但在结束文章之前,我想通过提供一种在按下next
键盘按钮后自动自动聚焦下一个输入的方法来改进表单组件,我认为这是确保良好用户体验的重要功能。
在 react-native 中聚焦输入的唯一方法是拥有一个ref
输入实例,并在您希望输入聚焦时调用焦点方法。
为了使其工作,我们需要访问所有输入引用,检测下一个输入并调用焦点方法。
我们可以通过在 Form 组件中使用 React Ref 来实现这一点Inputs
,然后我们逐个推送子输入 Refs。
为了聚焦下一个输入,我们需要实现onSubmitEditing
按下键盘按钮后调用的属性next
。我们需要通过调用下一个输入 Ref 的方法来聚焦focus
下一个输入,或者如果下一个输入 Ref 不存在则调用 blur。
值得一提的是,该onSubmitEditing
回调是在 blur 事件之后调用的。因此,如果键盘立即聚焦到下一个元素,它可能会失灵。最好将其设置blurOnSubmit={false}
为表单中的所有元素。
import * as React from 'react';
import {TextInput} from 'react-native';
import {ValidationOptions, FieldError} from 'react-hook-form';
interface ValidationMap {
[key: string]: ValidationOptions;
}
interface ErrorMap {
[key: string]: FieldError | undefined;
}
interface Props {
children: JSX.Element | JSX.Element[];
register: ({name}: {name: string}, validation: ValidationOptions) => void;
errors: ErrorMap;
validation: ValidationMap;
setValue: (name: string, value: string, validate?: boolean) => void;
}
export default ({
register,
errors,
setValue,
validation,
children,
}: Props) => {
const Inputs = React.useRef < Array < TextInput >> []
return (
<>
{(Array.isArray(children) ? [...children] : [children]).map(
(child, i) => {
return child.props.name
? React.createElement(child.type, {
...{
...child.props,
ref: (e: TextInput) => {
register(
{ name: child.props.name },
validation[child.props.name]
)
Inputs.current[i] = e
},
onChangeText: (v: string) =>
setValue(child.props.name, v, true),
onSubmitEditing: () => {
Inputs.current[i + 1]
? Inputs.current[i + 1].focus()
: Inputs.current[i].blur()
},
blurOnSubmit: false,
key: child.props.name,
error: errors[child.props.name],
},
})
: child
}
)}
</>
)
}
我们的最后一步是使用react-native-keyboard-aware-scroll-view组件来处理键盘外观并自动滚动到焦点 TextInput。
是的!我们的表单组件已经准备好投入生产了😎您可以在这个Snack Editor 项目中试用它。
//App.tsx
type FormData = {
name: string
email: string
password: string
}
export default () => {
const { handleSubmit, register, setValue, errors } = useForm<FormData>()
const onSubmit = (data: FormData) => {
Alert.alert('data', JSON.stringify(data))
}
return (
<KeyboardAwareScrollView contentContainerStyle={styles.container}>
<Hero />
<View style={styles.formContainer}>
<Form {...{ register, setValue, validation, errors }}>
<Input name="name" label="Name " />
<Input name="email" label="Email" />
<Input name="password" label="Password" secureTextEntry={true} />
<Button title="Submit" onPress={handleSubmit(onSubmit)} />
</Form>
</View>
</KeyboardAwareScrollView>
)
}
希望您觉得这篇文章有趣、信息丰富、引人入胜。我非常乐意在评论区听到您对此解决方案的评论和想法。
如果您认为其他人应该阅读这篇文章,请发推文、分享并在 Twitter 上关注我以获取下一篇文章。
鏂囩珷鏉ユ簮锛�https://dev.to/elaziziyoussouf/forms-in-react-native-the-right-way-4d46最初发表于https://obytes.com/