React表单输入校验时依赖state取旧值 如何基于最新值完成校验
问题原因
React的状态更新是异步批量执行的,调用setForm后,当前函数作用域内的form变量仍是本次渲染捕获的旧值,所以后续校验逻辑读取的自然是更新前的状态。
解决方案1:先构造新状态再校验(推荐)
不需要等待状态更新完成,直接基于当前拿到的输入值构造出新的表单对象,先完成校验逻辑,再一次性更新所有状态,避免异步问题。
const handleValidation = (e) => { const { name, value } = e.target; let errors = {}; let formIsValid = true; // 先构造新的表单对象 const newForm = { ...form, [name]: value, }; // 基于新表单对象做校验 //Name if (!newForm.name) { formIsValid = false; errors["name"] = "Cannot be empty"; } //Email if (!newForm.email) { formIsValid = false; errors["email"] = "Cannot be empty"; } //Message if (!newForm.message) { formIsValid = false; errors["message"] = "Cannot be empty"; } // 一次性更新所有状态 setForm(newForm); setError(JSON.stringify(errors)); setFormValid(formIsValid); };
该方案没有额外渲染开销,逻辑集中在同一个函数内,易维护。
解决方案2:用useEffect监听表单变化触发校验
如果需要把状态更新和校验逻辑分离,也可以把校验逻辑放到useEffect中,监听form的变化,每次form更新完成后自动执行校验:
// 仅负责更新表单状态 const handleChange = (e) => { const { name, value } = e.target; setForm((prevState) => ({ ...prevState, [name]: value, })); }; // 监听form变化,自动执行校验 useEffect(() => { let errors = {}; let formIsValid = true; if (!form.name) { formIsValid = false; errors["name"] = "Cannot be empty"; } if (!form.email) { formIsValid = false; errors["email"] = "Cannot be empty"; } if (!form.message) { formIsValid = false; errors["message"] = "Cannot be empty"; } setError(JSON.stringify(errors)); setFormValid(formIsValid); }, [form]);
该方案适合校验逻辑复杂、存在多字段联动校验的场景,逻辑解耦更清晰。
内容的提问来源于stack exchange,提问作者Lajos Bela
相关产品推荐
相关产品推荐

