React表单onChange触发setValues后重载,无法修改字段求助
问题描述
创建可复用表单字段时,触发onChange执行setValues后,表单出现类似重载的现象,无法继续修改字段。相关代码如下:
BuildForm.js(自定义Hook文件)
import { useState } from 'react'; const BuildForm = () => { const [values, setValues] = useState({}); const [errors, setErrors] = useState({}); const validate = (name, value) => { switch (name) { case 'first_name': if(value.trim() === ''){ errors[name] = 'First Name is Rrequired.'; setErrors(errors); } else{ errors[name] = ''; setErrors(errors); } break; case 'last_name': if(value.trim() === ''){ setErrors({...errors, [name]:'Last Name is Rrequired.'}); } else{ setErrors({...errors, [name]:''}); } break; default: break; } }; const handleChange = (event) => { // event.persist(); let name = event.target.name; let value = event.target.value; validate(name, value); setValues({...values, [name]:value}); // this is line with the issue console.log(name, value); }; const Name = (props) => { return( <div className="fullwidth"> <p className="description"><strong>{props.label}</strong></p> <div className="firstname halfwidth left"> <label htmlFor="first_name">FIRST</label> <input type="text" name="first_name" onChange={handleChange}/> { errors.first_name && <p className="validation">{errors.first_name}</p> } </div> <div className="lastname halfwidth right"> <label htmlFor="last_name">LAST</label> <input type="text" name="last_name" onChange={handleChange} /> { errors.last_name && <p className="validation">{errors.last_name}</p> } </div> </div> ); }; return { Name, } }; export default BuildForm;
FirstForm.js(表单页面文件)
import './App.css'; import React, {useState} from 'react'; import { Link, Routes, Route, useNavigate } from 'react-router-dom'; import BuildForm from './Hooks/BuildForm'; function FirstForm() { const navigate = useNavigate(); const MyForm = BuildForm(); return ( <div className="App"> <div id="header"> <img src={logo} className="logo" alt="logo"/> </div> <div id="pagebody"> <div id="formcontainer"> <form id="myForm"> <MyForm.Name label="Client Name"/> <div className="submitbutton fullwidth"><input type="submit" /></div> </form> </div> </div> </div> ); } export default FirstForm;
问题原因
- 组件定义在Hook内部:
Name组件被定义在BuildFormHook内部,每次Hook的状态(values/errors)更新时,Hook会重新执行,生成全新的Name组件。React会将其识别为不同的组件类型,卸载旧组件并挂载新组件,导致输入框丢失焦点、状态重置,无法继续输入。 - 状态更新不规范:
first_name校验逻辑中直接修改errors状态对象后调用setErrors,未使用不可变更新,React无法正确检测状态变化,可能引发渲染异常。 - 未阻止表单默认提交:表单提交按钮未阻止默认行为,可能触发页面刷新,表现为表单重载。
修复方案
以下是修复后的完整代码:
修复后的BuildForm.js
import { useState, useCallback, useMemo } from 'react'; const BuildForm = () => { const [values, setValues] = useState({}); const [errors, setErrors] = useState({}); // 用useCallback缓存校验函数,避免不必要重渲染 const validate = useCallback((name, value) => { // 不可变更新errors,创建新对象 const newErrors = { ...errors }; switch (name) { case 'first_name': newErrors[name] = value.trim() === '' ? 'First Name is Required.' : ''; break; case 'last_name': newErrors[name] = value.trim() === '' ? 'Last Name is Required.' : ''; break; default: break; } setErrors(newErrors); }, [errors]); // 用useCallback缓存handleChange,确保引用稳定 const handleChange = useCallback((event) => { const { name, value } = event.target; validate(name, value); // 函数式更新values,确保获取最新状态 setValues(prevValues => ({ ...prevValues, [name]: value })); console.log(name, value); }, [validate]); // 用useMemo缓存Name组件,确保组件引用不变 const Name = useMemo(() => (props) => { return( <div className="fullwidth"> <p className="description"><strong>{props.label}</strong></p> <div className="firstname halfwidth left"> <label htmlFor="first_name">FIRST</label> <input type="text" name="first_name" onChange={handleChange}/> { errors.first_name && <p className="validation">{errors.first_name}</p> } </div> <div className="lastname halfwidth right"> <label htmlFor="last_name">LAST</label> <input type="text" name="last_name" onChange={handleChange} /> { errors.last_name && <p className="validation">{errors.last_name}</p> } </div> </div> ); }, [handleChange, errors]); return { Name, } }; export default BuildForm;
修复后的FirstForm.js
import './App.css'; import React from 'react'; import { useNavigate } from 'react-router-dom'; import BuildForm from './Hooks/BuildForm'; function FirstForm() { const navigate = useNavigate(); // 直接解构获取Name组件,避免多余的对象包装 const { Name } = BuildForm(); // 阻止表单默认提交行为,避免页面刷新 const handleSubmit = (e) => { e.preventDefault(); // 可在此添加表单提交逻辑 }; return ( <div className="App"> <div id="header"> <img src={logo} className="logo" alt="logo"/> </div> <div id="pagebody"> <div id="formcontainer"> <form id="myForm" onSubmit={handleSubmit}> <Name label="Client Name"/> <div className="submitbutton fullwidth"><input type="submit" /></div> </form> </div> </div> </div> ); } export default FirstForm;
关键修复点说明
- 缓存组件与回调:通过
useMemo缓存Name组件,确保Hook状态更新时组件引用稳定,避免React卸载重建;用useCallback缓存handleChange和validate,减少不必要的重渲染。 - 不可变状态更新:校验函数中通过展开运算符创建新的
errors对象,避免直接修改原状态;setValues使用函数式更新,确保获取最新的状态值。 - 阻止表单默认提交:添加
handleSubmit函数阻止表单默认刷新行为,避免页面重载。
内容的提问来源于stack exchange,提问作者Martina
相关产品推荐
相关产品推荐

