React Native 中的表单,正确的方法😎

2025-06-11

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.createElementAPI 为每个具有属性的子项创建新的 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://obytes.com/

鏂囩珷鏉ユ簮锛�https://dev.to/elaziziyoussouf/forms-in-react-native-the-right-way-4d46
PREV
初级前端开发者六个月的十个心得:你需要花费数周甚至数月的时间来理解代码库。这没关系。忽略你的副业项目也没关系。不要低估你的“软”技能。你的价值在于你带来的不同视角。勇于冒险。勇于表达。你不必孤军奋战。找到你的社群。同理心并非这份工作的必要条件,但你需要能够倾听用户的需求。放慢工作节奏。优先安排睡眠时间,并定期喝咖啡休息。记住,可能性无穷无尽。写下你成为开发者的动机。心情不好的时候可以参考一下。
NEXT
如何使用 Jest 模拟导入的函数