修改MUI TextField提示文本时触发输入值清空问题排查
问题与解决方案
问题描述
我开发了一个用于收集用户信息的MUI对话框,点击“提交”按钮两次后,result对象的属性会被清空。需要在保留校验逻辑的前提下解决该问题。
原代码如下:
function UserInfoDialog(props) { const [nameErrorMsg, setNameErrorMsg] = useState(null); const [emailErrorMsg, setEmailErrorMsg] = useState(null); const [phoneErrorMsg, setPhoneErrorMsg] = useState(null); var result = {}; const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/g; const phoneRegex = /^(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/g; const onSubmit = () => { console.log("result:", result); if (!result.name) { setNameErrorMsg("Name is required"); return; } else { setNameErrorMsg(null); } if (!result.email && !result.phone) { setEmailErrorMsg("Either phone or email is required"); setPhoneErrorMsg("Either phone or email is required"); return; } else if (result.email && !emailRegex.test(result.email)) { setEmailErrorMsg("Please enter a valid email address"); return; } else if (result.phone && !phoneRegex.test(result.phone)) { setPhoneErrorMsg("Please enter a valid phone number"); return; } else { setEmailErrorMsg(null); setPhoneErrorMsg(null); } //Todo: submit the result props.onClose(); }; return ( <Dialog open={props.open} onClose={() => props.onClose()}> <DialogContent> <h4>Please fill out the following information so we can get you connected</h4> <TextField autoFocus fullWidth margin="dense" label="Name" variant="standard" helperText={nameErrorMsg} error={nameErrorMsg != null} onChange={e => result.name = e.target.value}/> <TextField fullWidth margin="dense" label="Email" variant="standard" helperText={emailErrorMsg} error={emailErrorMsg != null} onChange={e => result.email = e.target.value}/> <TextField fullWidth margin="dense" label="Phone" variant="standard" helperText={phoneErrorMsg} error={phoneErrorMsg != null} onChange={e => result.phone = e.target.value}/> </DialogContent> <DialogActions> <Button onClick={() => props.onClose()}>Cancel</Button> <Button onClick={() => onSubmit()}>Submit</Button> </DialogActions> </Dialog> ); }
问题原因
- 普通变量无法保留状态:
var result = {}是组件函数内部的普通变量,每次组件因状态更新(比如调用setNameErrorMsg)重渲染时,该变量都会被重新初始化为空对象。第一次提交触发校验错误后,状态更新导致组件重渲染,result被重置,第二次提交时自然为空。 - 正则全局标志导致匹配异常:正则表达式使用了
g全局标志,test方法会记录上次匹配的位置,多次调用会出现匹配结果异常,进而触发不必要的状态更新,加重组件重渲染问题。
解决方案
修改后的代码如下:
function UserInfoDialog(props) { const [nameErrorMsg, setNameErrorMsg] = useState(null); const [emailErrorMsg, setEmailErrorMsg] = useState(null); const [phoneErrorMsg, setPhoneErrorMsg] = useState(null); // 用useState管理表单数据,确保重渲染时数据不丢失 const [result, setResult] = useState({ name: '', email: '', phone: '' }); // 移除正则的g标志,避免test方法的异常行为 const emailRegex = /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/; const phoneRegex = /^(\+\d{1,2}\s?)?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/; const onSubmit = () => { console.log("result:", result); if (!result.name.trim()) { setNameErrorMsg("Name is required"); return; } else { setNameErrorMsg(null); } if (!result.email.trim() && !result.phone.trim()) { setEmailErrorMsg("Either phone or email is required"); setPhoneErrorMsg("Either phone or email is required"); return; } else if (result.email.trim() && !emailRegex.test(result.email.trim())) { setEmailErrorMsg("Please enter a valid email address"); return; } else if (result.phone.trim() && !phoneRegex.test(result.phone.trim())) { setPhoneErrorMsg("Please enter a valid phone number"); return; } else { setEmailErrorMsg(null); setPhoneErrorMsg(null); } // Todo: submit the result props.onClose(); }; // 封装状态更新方法,简化逻辑 const updateField = (field, value) => { setResult(prev => ({ ...prev, [field]: value })); }; return ( <Dialog open={props.open} onClose={() => props.onClose()}> <DialogContent> <h4>Please fill out the following information so we can get you connected</h4> <TextField autoFocus fullWidth margin="dense" label="Name" variant="standard" helperText={nameErrorMsg} error={nameErrorMsg != null} value={result.name} onChange={e => updateField('name', e.target.value)} /> <TextField fullWidth margin="dense" label="Email" variant="standard" helperText={emailErrorMsg} error={emailErrorMsg != null} value={result.email} onChange={e => updateField('email', e.target.value)} /> <TextField fullWidth margin="dense" label="Phone" variant="standard" helperText={phoneErrorMsg} error={phoneErrorMsg != null} value={result.phone} onChange={e => updateField('phone', e.target.value)} /> </DialogContent> <DialogActions> <Button onClick={() => props.onClose()}>Cancel</Button> <Button onClick={onSubmit}>Submit</Button> </DialogActions> </Dialog> ); }
关键修改点
- 使用
useState声明result状态,替代普通变量,确保组件重渲染时表单数据不会丢失。 - 移除正则表达式的
g标志,避免test方法因全局匹配导致的异常。 - 为每个
TextField绑定value属性,实现受控组件,保证表单数据与状态同步。 - 封装
updateField方法,简化状态更新逻辑,提高代码可维护性。
内容的提问来源于stack exchange,提问作者derekantrican
相关产品推荐
相关产品推荐

