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

React MUI提交按钮需点击两次触发函数的问题求助

问题解决:React MUI表单两次提交+数据校验实现

一、两次提交问题的根源

你在submitData中先通过setState把userName等字段复制到guestName系列变量,但React的setState是异步操作,紧接着读取this.state.guestName时,新值还未生效。第一次点击时校验逻辑用的是旧的空值,第二次点击时guestName才完成更新,导致需要两次点击才能触发正常提交。

二、优化方案

1. 简化状态结构

不需要额外维护guestName、guestEmail、guestMobile,直接使用绑定输入框的userName、email、mobile字段即可,彻底避免异步更新带来的状态不同步问题。

2. 实现完整数据校验

提交前统一校验:

  • 所有输入字段非空
  • 邮箱格式符合标准规则
  • 手机号为10位纯数字

三、修改后的完整代码

constructor(props){
    super(props);
    this.state={
        userName: "",
        email: "",
        mobile: "",
        refCode: "",
        dataLoaded: false,
        refStatus: false,
        cardNo: ""
    }

    this.submitData = this.submitData.bind(this);
    this.getRefCode = this.getRefCode.bind(this);
}

setUsername = event => {
    this.setState({ userName: event.target.value });
}

setEmail = event => {
    this.setState({ email: event.target.value });
}

setMobile = event => {
    this.setState({ mobile: event.target.value });
}

// 统一校验函数
validateForm() {
    const { userName, email, mobile } = this.state;
    // 非空校验
    if (!userName.trim()) {
        alert('请输入姓名');
        return false;
    }
    if (!email.trim()) {
        alert('请输入邮箱');
        return false;
    }
    // 邮箱格式校验
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!emailRegex.test(email)) {
        alert('请输入合法的邮箱格式');
        return false;
    }
    // 手机号校验:10位纯数字
    const mobileRegex = /^\d{10}$/;
    if (!mobileRegex.test(mobile)) {
        alert('请输入10位数字的手机号');
        return false;
    }
    return true;
}

submitData(e){
    e.preventDefault();
    // 校验不通过则终止提交
    if (!this.validateForm()) {
        return;
    }
    // 校验通过后调用API
    this.getRefCode();
};

getRefCode(){
    const firstname = this.state.userName
    const lastname = "fonseka"
    const email = this.state.email
    const mobile = this.state.mobile

    let customerData = {
        "firstName": firstname,
        "lastName": lastname,
        "email": email,
        "mobileNumber": mobile
    }

    let seatDetails = [];
    let seatNos = selection.seats.split(',')
    let seatCatObj = {
        "seatCategoryId" : parseInt(selection.seatType),
        "seatNumbers" : seatNos,
        "adultCount" : parseInt(qty.length),
        "childCount" : parseInt(0)
    }
    seatDetails.push(seatCatObj);

    fetch(`${process.env.REACT_APP_DEV_BASE_URL}/v1/booking`, {
        method: 'POST',
        headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'client_token': 'ebd86470-7e90-4ece-9e89-1b6d4d2cbb61',
            'access_token': 'FFF',
        },
        
        body: JSON.stringify({
            movieId: parseInt(selection.eventId),
            venueId: parseInt(selection.venueId),
            showtimeId: parseInt(selection.showId),
            movieDate: selection.showDate,
            seatDetails: seatDetails,
            customerDetails: customerData,
        })
    })
    .then((response) => response.json())
    .then((data) => {
        if(data.success){
            this.setState({
                refCode: data.data.refcode,
                dataLoaded: true,
                refStatus: data.success
            }, () => {
                // 在setState回调中读取最新refCode,避免异步问题
                const totPrice = selection.price * qty.length
                window.location.href = `${process.env.REACT_APP_DEV_BASE_URL_PAYMENT}?userId=${505}&reference=${this.state.refCode}&amount=${totPrice}&currency=LKR&returnUrl=${encodeURIComponent(returUrl)}&cardId=${this.state.cardNo}`
            })
        } else {
            window.location.href = '#/BookingFailed?error=2'
        }
    }).catch((err) => console.log(err))          
}

render() {
    return(
        <>
            <TextField
                autoFocus
                required 
                margin="dense"
                id="Username"
                label="Name"
                type="text"
                value={this.state.userName}
                onChange={this.setUsername}
            />
            <br/>
            <TextField
                required 
                margin="dense"
                id="email"
                label="Email"
                type="email"
                value={this.state.email}
                onChange={this.setEmail}
            />
            <br/>
            <TextField
                required 
                margin="dense"
                id="mobile"
                label="Mobile"
                type="tel" // 替换mobile为标准tel类型,适配移动端
                value={this.state.mobile}
                onChange={this.setMobile}
            />
            <button onClick={this.submitData}>提交</button>
        </>
    )
}

四、关键修改说明

  • 移除冗余的guest系列状态字段,直接使用输入绑定的原始字段,解决异步更新导致的状态不同步
  • 新增validateForm函数集中处理所有校验逻辑,提前拦截非法输入
  • 手机号输入框改为tel类型,优化移动端输入体验
  • 在setState回调中处理跳转逻辑,确保读取到最新的refCode值

内容的提问来源于stack exchange,提问作者Ryan Fonseka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 00:27:05