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}¤cy=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
相关产品推荐
相关产品推荐

