react-hook-form提交时state绑定输入值无法检测报错如何解决
错误原因
NaN警告
- 初始化的
price状态值为空字符串,当用户清空输入框或输入非数字内容时,parseInt(ev.target.value)会返回NaN,将NaN赋值给输入框的value属性就会触发该警告。
表单取不到voucherPrice值
- 你同时使用了自定义
state管理输入值和react-hook-form管控表单,但仅将register返回的引用绑定到了输入框的innerRef,react-hook-form没有收到值更新的通知:你自定义的onChange事件仅更新了本地的price状态,没有同步给表单,因此提交时会触发必填校验错误。
修复方案
- 从
useForm中导出setValue方法,用于将本地状态同步到表单 - 新增
useEffect监听price变化,自动同步到表单,不需要在每个值变更的地方重复写同步逻辑 - 优化数字转换逻辑,避免返回
NaN
完整修改后代码
import React from "react"; import { useStylesPriceSelection } from "../../styles/stepStyles"; import clsx from "clsx"; import { Input } from "reactstrap"; import { useState, useEffect } from "react"; import { useForm } from "react-hook-form"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import Button from "@mui/material/Button"; const schema = yup .object({ voucherPrice: yup.number().positive().required(), }) .required(); function PriceSelection(props) { const { register, handleSubmit, setValue, formState: { errors }, } = useForm({ resolver: yupResolver(schema), }); const onSubmit = (data) => console.log(data); const classes = useStylesPriceSelection(); const [selected, setSelected] = useState(false); const [price, setPrice] = useState(""); // 监听price变化,自动同步到react-hook-form useEffect(() => { setValue("voucherPrice", price, { shouldValidate: true }); }, [price, setValue]); const handleSelectedBox = (ev) => { console.log("", price); setSelected(ev.target.getAttribute("data-boxprice")); const priceVal = parseInt(ev.target.getAttribute("data-price")); setPrice(isNaN(priceVal) ? "" : priceVal); }; const handlePriceInputChange = (ev) => { console.log("change", price); const inputVal = ev.target.value; const priceVal = inputVal ? parseInt(inputVal) : ""; setPrice(isNaN(priceVal) ? "" : priceVal); }; const priceBox1 = 25; const priceBox2 = 50; const priceBox3 = 75; const priceBox4 = 100; return ( <div> <section className={classes.priceBoxWrap}> <div data-boxprice="1" data-price={`${priceBox1}`} onClick={(ev) => handleSelectedBox(ev)} className={clsx( classes.priceBox, ((selected === "1" && price === priceBox1) || price === priceBox1) && classes.priceBoxSelected )} > {`${priceBox1}€`} </div> <div data-boxprice="2" data-price={`${priceBox2}`} onClick={(ev) => handleSelectedBox(ev)} className={clsx( classes.priceBox, ((selected === "2" && price === priceBox2) || price === priceBox2) && classes.priceBoxSelected )} > {`${priceBox2}€`} </div> </section> <section className={classes.priceBoxWrap}> <div data-boxprice="3" data-price={`${priceBox3}`} onClick={(ev) => handleSelectedBox(ev)} className={clsx( classes.priceBox, ((selected === "3" && price === priceBox3) || price === priceBox3) && classes.priceBoxSelected )} > {`${priceBox3}€`} </div> <div data-boxprice="4" data-price={`${priceBox4}`} onClick={(ev) => handleSelectedBox(ev)} className={clsx( classes.priceBox, ((selected === "4" && price === priceBox4) || price === priceBox4) && classes.priceBoxSelected )} > {`${priceBox4}€`} </div> </section> <form onSubmit={handleSubmit(onSubmit)}> <Input onChange={handlePriceInputChange} type="number" value={price} innerRef={register("voucherPrice", { required: true })} ></Input> <p>{errors.voucherPrice?.message}</p> <Button variant="contained" sx={{ mt: 1, mr: 1 }} type="submit" > Continue </Button> </form> </div> ); } export default PriceSelection;
内容的提问来源于stack exchange,提问作者Gotey
相关产品推荐
相关产品推荐

