如何用React+React Hook Form+Yup创建可复用表单组件及解决Ref报错
问题解决:React Hook Form 结合自定义 InputGroup 组件的 Ref 错误
错误原因
你遇到的Warning: Unexpected ref object provided for input错误,是因为错误地将React Hook Form的register返回对象直接赋值给了自定义组件的ref属性。register返回的是包含ref、name、onChange等属性的对象,需要将这些属性全部传递给组件,而非仅作为ref使用。同时原自定义组件未支持传入子元素(如密码显示按钮的InputLeftElement),导致结构错误。
分步修复
1. 修改自定义InputGroups组件
更新组件以支持接收并传递表单相关属性,同时支持子元素渲染:
import { Input, InputGroup, InputRightElement } from "@chakra-ui/react"; import { forwardRef } from "react"; const InputGroups = forwardRef(({ size, type, placeholder, icon, children, ...rest }, ref) => { return ( <InputGroup my={4} size={size}> {/* 渲染传入的左侧元素(如密码显示按钮) */} {children} <Input ref={ref} variant="filled" pr={10} type={type} placeholder={placeholder} {...rest} // 传递register返回的name、onChange、onBlur等核心属性 /> <InputRightElement>{icon}</InputRightElement> </InputGroup> ); }); export default InputGroups;
2. 修复Login组件的表单绑定
- 正确传递
register返回的所有属性给InputGroups组件 - 补全缺失的导入(
useState、InputLeftElement) - 添加表单验证错误提示
import { useState } from "react"; import { useForm } from "react-hook-form"; import * as Yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { Button, useToast, InputLeftElement } from "@chakra-ui/react"; import InputGroups from "./InputGroups"; // 根据你的文件路径调整 const schema = Yup.object().shape({ personalNumber: Yup.number().required("请输入您的号码"), password: Yup.string().required("请输入密码"), }); const Login = () => { const toast = useToast({ containerStyle: { marginTop: "40px", }, }); const [show, setShow] = useState(false); const showPasswordHandleClick = () => setShow(!show); const { register, handleSubmit, formState: { errors } } = useForm({ resolver: yupResolver(schema), defaultValues: { personalNumber: "", password: "" } }); const onSubmitHandler = (data) => { toast({ title: "登录成功", status: "success", duration: 9000, isClosable: true, position: "top-right", }); console.log(data); }; return ( <form className="login-form" style={{ textAlign: "center" }} onSubmit={handleSubmit(onSubmitHandler)} > <InputGroups size="md" type="number" placeholder="Type Your Number Code" icon={ <i className="ki-duotone ki-sms"> <i className="path1"></i> <i className="path2"></i> </i> } {...register("personalNumber")} // 传递register的所有属性 /> {/* 显示验证错误 */} {errors.personalNumber && ( <p style={{ color: "red", margin: "0 0 10px 0" }}>{errors.personalNumber.message}</p> )} <InputGroups size="md" type={show ? "text" : "password"} placeholder="ُType Password..." icon={ <i className="ki-duotone ki-lock-2"> <i className="path1"></i> <i className="path2"></i> <i className="path3"></i> <i className="path4"></i> <i className="path5"></i> </i> } {...register("password")} // 传递register的所有属性 > <InputLeftElement> <Button fontSize={10} onClick={showPasswordHandleClick}> {show ? "hide" : "show"} </Button> </InputLeftElement> </InputGroups> {/* 显示验证错误 */} {errors.password && ( <p style={{ color: "red", margin: "0 0 10px 0" }}>{errors.password.message}</p> )} <Button className="login-btn" type="submit" mt={10} colorScheme="teal" size="lg" > login </Button> </form> ); }; export default Login;
关键说明
forwardRef的作用是将外部组件的ref传递给内部的Input元素,确保React Hook Form能正确获取输入框DOM节点{...rest}用于传递register返回的name、onChange、onBlur等属性,这是React Hook Form跟踪表单状态的核心- 新增的错误提示可以让用户直观看到验证失败的原因
内容的提问来源于stack exchange,提问作者abbas deylami
相关产品推荐
相关产品推荐

