You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

修改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>
    );
}

问题原因

  1. 普通变量无法保留状态:var result = {}是组件函数内部的普通变量,每次组件因状态更新(比如调用setNameErrorMsg)重渲染时,该变量都会被重新初始化为空对象。第一次提交触发校验错误后,状态更新导致组件重渲染,result被重置,第二次提交时自然为空。
  2. 正则全局标志导致匹配异常:正则表达式使用了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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 16:25:39