React Native中React Hook Form搭配Yup校验不生效及用法咨询
问题排查&解决
你的yup校验不生效是代码里的几个错误导致的,修复后即可正常运行:
- 第一个邮箱输入框的
Controller的name属性写的是大写开头的Email,和yup schema里定义的小写email字段名不匹配,yup找不到对应字段自然不会触发校验 - 不需要在
useEffect里手动调用register注册字段,Controller组件会自动帮你完成字段注册,重复注册会导致逻辑冲突 - 密码、确认密码输入框的
Controller里没有用field返回的onChange、onBlur方法,自己手动写setValue会丢失失焦触发校验的能力,也不符合组件的设计规范 - 提交函数
enviar里打印的变量名写错了,参数是dados,你写的data会报错
疑问解答
1. yup是react-hook-form的必选项吗?
不是。react-hook-form本身自带轻量的校验能力,就是你写在Controller的rules属性里的规则,完全可以满足简单的校验需求。只有当你需要处理复杂校验逻辑(比如跨字段联动校验、统一管理全局校验规则、自动TS类型推导、更复杂的格式校验)的时候,才需要搭配yup/zod这类schema校验库使用。
2. yup和react-hook-form搭配的正确使用步骤
你已经走对了大部分流程,只要注意这几点即可:
- 定义yup schema的时候,字段名必须和
Controller的name属性完全一致,大小写也要对应 - 在
useForm的配置项里传入resolver: yupResolver(你的schema)即可,不需要额外做其他配置 - 校验错误会统一挂载在
formState.errors下,key就是你定义的字段名,错误提示就是yup里写的提示内容
3. Controller的rules属性怎么定义
rules是react-hook-form内置的校验配置项,常见的配置如下:
rules={{ // 必填校验,值为字符串的话就是错误提示 required: "邮箱不能为空", // 字符串最小/最大长度 minLength: {value: 8, message: "密码最少8位"}, maxLength: {value: 20, message: "密码最多20位"}, // 数字的最小/最大值 min: {value: 18, message: "年龄不能小于18岁"}, // 正则校验 pattern: {value: /^1[3-9]\d{9}$/, message: "请输入正确的手机号"}, // 自定义校验函数,返回true就是校验通过,返回字符串就是错误提示 validate: value => value === 密码字段的值 || "两次输入的密码不一致" }}
如果同时配置了yup resolver和rules属性,两类规则都会生效,rules的校验优先级更高,任意规则不通过都会返回错误。
修正后的完整代码
import React from "react"; import { View, TextInput, TouchableOpacity, Text, StyleSheet, } from "react-native"; import { useForm, Controller } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; const schema = yup.object({ email: yup.string().email("请输入正确的邮箱格式").required("邮箱不能为空"), senha: yup .string() .min(8, "密码最少需要8位字符"), confirmacaoSenha: yup .string() .oneOf([yup.ref('senha'), null], "两次输入的密码不一致") }); const AcessoFormulario = ({ tipoUsuario }) => { const { control, handleSubmit, formState: { errors }, } = useForm({ resolver: yupResolver(schema), defaultValues: { email: "", senha: "", confirmacaoSenha: "", }, }); const enviar = (dados) => { console.log(dados); //TODO firebase auth }; return ( <View style={styles.container}> <View style={styles.subContainer}> <Text style={styles.label}>E-mail:</Text> <Controller control={control} render={({ field: { onChange, onBlur, value } }) => ( <TextInput style={styles.input} onBlur={onBlur} onChangeText={onChange} value={value} keyboardType="email-address" autoCapitalize="none" /> )} name="email" /> {errors.email && <Text>{errors.email.message}</Text>} </View> <View style={styles.subContainer}> <Text style={styles.label}>Senha:</Text> <Controller control={control} render={({ field: { onChange, onBlur, value } }) => ( <TextInput style={styles.input} onBlur={onBlur} onChangeText={onChange} value={value} secureTextEntry /> )} name="senha" /> {errors.senha && <Text>{errors.senha.message}</Text>} </View> <View style={styles.subContainer}> <Text style={styles.label}>Confirmação de senha:</Text> <Controller control={control} render={({ field: { onChange, onBlur, value } }) => ( <TextInput style={styles.input} onBlur={onBlur} onChangeText={onChange} value={value} secureTextEntry /> )} name="confirmacaoSenha" /> {errors.confirmacaoSenha && <Text>{errors.confirmacaoSenha.message}</Text>} </View> <TouchableOpacity style={styles.btnEnviar} onPress={handleSubmit(enviar)}> <Text style={styles.btnTexto}>Registrar-se</Text> </TouchableOpacity> </View> ); }; const styles = StyleSheet.create({ container: { display: "flex", flexDirection: "column", marginTop: 40, margin: 10, padding: 10, backgroundColor: "#77d477", borderRadius: 20, }, input: { borderWidth: 2, borderColor: "#ffffff", borderRadius: 6, margin: 2, paddingHorizontal: 8, paddingVertical: 4 }, label: { color: "#ffffff", fontWeight: "bold", fontSize: 15, marginBottom: 4 }, subContainer: { margin: 15, }, btnEnviar: { backgroundColor: "#ffffff", margin: 15, padding: 10, borderRadius: 6 }, btnTexto: { color: "#77d477", textAlign: "center", fontWeight: "bold", fontSize: 20, }, }); export default AcessoFormulario;
内容的提问来源于stack exchange,提问作者nanquim
相关产品推荐
相关产品推荐

