You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用React Hook Form实现React Native多屏表单提交与校验?

React Native 多屏表单实现方案(React Hook Form)

核心实现思路

  • 借助React Hook Form的全局校验能力,结合React Navigation实现错误屏幕自动跳转
  • 所有表单字段共享同一个control实例,保证跨屏幕的数据与校验状态统一
  • 提交时触发全量校验,根据错误字段定位并导航到对应屏幕

完整代码实现

1. 表单Hook封装(可单独存为useContractForm.ts)

import { useForm, yupResolver } from 'react-hook-form';
import * as yup from 'yup';

// 定义表单数据类型
type ContractFormData = {
  screen1Field: string;
  screen2Field: number;
  screen3Field: boolean;
};

// Yup校验规则
const contractFormSchema = yup.object().shape({
  screen1Field: yup.string().required('首屏字段必填'),
  screen2Field: yup.number().min(10, '第二屏字段不能小于10').required('第二屏字段必填'),
  screen3Field: yup.boolean().oneOf([true], '第三屏必须勾选'),
});

export const useContractForm = () => {
  const {
    control,
    reset,
    handleSubmit,
    setValue,
    getValues,
    formState: { errors },
    trigger,
  } = useForm<ContractFormData>({
    resolver: yupResolver(contractFormSchema),
    mode: 'all',
  });

  return {
    handleSubmit,
    getValues,
    setValue,
    control,
    errors,
    reset,
    trigger,
  };
};

2. 首屏组件(含提交按钮)

import React from 'react';
import { View, TextInput, Button, Text } from 'react-native';
import { Controller } from 'react-hook-form';
import { useContractForm } from './useContractForm';
import { useNavigation } from '@react-navigation/native';

const Screen1 = () => {
  const { control, errors, handleSubmit } = useContractForm();
  const navigation = useNavigation();

  // 最终提交逻辑
  const onSubmit = (data: ContractFormData) => {
    console.log('提交的表单数据:', data);
    // 这里可添加API调用等业务逻辑
  };

  // 带错误跳转的提交处理
  const handleFormSubmit = async () => {
    const isValid = await handleSubmit(onSubmit)();
    
    if (!isValid) {
      // 根据错误字段导航到对应屏幕
      if (errors.screen2Field) {
        navigation.navigate('Screen2');
      } else if (errors.screen3Field) {
        navigation.navigate('Screen3');
      }
      // 首屏字段错误则留在当前页展示
    }
  };

  return (
    <View style={{ padding: 20 }}>
      <Controller
        control={control}
        name="screen1Field"
        render={({ field: { onChange, value } }) => (
          <TextInput
            placeholder="首屏输入字段"
            value={value}
            onChangeText={onChange}
            style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
          />
        )}
      />
      {errors.screen1Field && (
        <Text style={{ color: 'red', marginBottom: 20 }}>{errors.screen1Field.message}</Text>
      )}

      <Button title="提交表单" onPress={handleFormSubmit} />
    </View>
  );
};

export default Screen1;

3. 第二屏组件

import React from 'react';
import { View, TextInput, Text } from 'react-native';
import { Controller } from 'react-hook-form';
import { useContractForm } from './useContractForm';

const Screen2 = () => {
  const { control, errors } = useContractForm();

  return (
    <View style={{ padding: 20 }}>
      <Controller
        control={control}
        name="screen2Field"
        render={({ field: { onChange, value } }) => (
          <TextInput
            placeholder="第二屏数字字段"
            value={value?.toString() || ''}
            onChangeText={(text) => onChange(Number(text))}
            keyboardType="numeric"
            style={{ borderWidth: 1, padding: 10, marginBottom: 10 }}
          />
        )}
      />
      {errors.screen2Field && (
        <Text style={{ color: 'red', marginBottom: 20 }}>{errors.screen2Field.message}</Text>
      )}
    </View>
  );
};

export default Screen2;

4. 第三屏组件

import React from 'react';
import { View, Switch, Text } from 'react-native';
import { Controller } from 'react-hook-form';
import { useContractForm } from './useContractForm';

const Screen3 = () => {
  const { control, errors } = useContractForm();

  return (
    <View style={{ padding: 20 }}>
      <Controller
        control={control}
        name="screen3Field"
        render={({ field: { onChange, value } }) => (
          <View style={{ flexDirection: 'row', alignItems: 'center', marginBottom: 10 }}>
            <Text>第三屏勾选框:</Text>
            <Switch value={value || false} onValueChange={onChange} />
          </View>
        )}
      />
      {errors.screen3Field && (
        <Text style={{ color: 'red', marginBottom: 20 }}>{errors.screen3Field.message}</Text>
      )}
    </View>
  );
};

export default Screen3;

关键说明

  • 所有屏幕共用同一个useContractForm实例,确保数据和校验状态全局同步
  • 提交时通过handleSubmit触发全量校验,校验不通过时根据errors对象中的错误字段跳转对应屏幕
  • 每个字段通过Controller组件与React Hook Form绑定,同步输入值并触发校验规则
  • 错误信息直接在对应屏幕的输入组件下方展示,用户修改后校验状态自动更新

内容的提问来源于stack exchange,提问作者Narek Hovhannisyan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 23:08:23