React函数组件表单校验器失效,输入非法仍发送API请求是什么原因?
问题原因
- setError是异步更新方法:React 的 useState 返回的更新函数
setError是异步生效的,调用setError后当前渲染周期内的error变量仍然是更新前的旧值(第一次校验时旧值为空字符串),所以后续判断error !== ''的逻辑永远拿不到刚设置的错误信息,直接进入resolve分支触发API请求。 - 邮箱正则表达式书写错误:你定义的正则
RegExp('w+@example.com')中的w会被识别为普通字符,仅能匹配以字母w开头的邮箱,而非你预期的「匹配任意数字、字母、下划线」,如果要使用元字符\w,在字符串写法里需要转义为\\w,另外正则里的.也需要转义,避免被识别为匹配任意字符的元字符,更推荐直接使用正则字面量写法降低出错概率。 - 表单数据存储方式错误:你把
data定义为组件内的普通变量,每次组件重新渲染时data都会被重置为空对象,存在数据丢失的风险,应该使用useState存储表单数据。
修复方案
直接调整校验器的逻辑,不需要依赖异步更新的error状态做判断,在校验器内部定义临时错误变量判断即可,同时修正正则和表单数据的存储方式,核心修改后的代码如下:
import React, { useState } from 'react'; import Button from '@material-ui/core/Button'; import { makeStyles } from '@material-ui/styles'; import TextField from '@material-ui/core/TextField'; const useStyles = makeStyles(() => ({ avatar: { margin: '10px auto', backgroundColor: 'green', }, paper: { width: '50vw', margin: '1rem auto 3rem auto', padding: '2rem', display: 'flex', flexDirection: 'column', backgroundColor: 'blue', backdropFilter: 'blur(10px)', }, align: { marginLeft: 'auto', marginRight: 'auto', }, field: { width: '100%', marginLeft: 'auto', marginRight: 'auto', marginTop: '15px', }, form: { width: '80%', display: 'flex', flexDirection: 'column', margin: '1rem auto', }, })); export default function Signup() { const classes = useStyles(); const [error, setError] = useState(''); // 把表单数据改为state存储,避免重渲染丢失数据 const [data, setData] = useState({ username: '', email: '', first_name: '', last_name: '', password: '', password2: '' }); function updateData(event, key) { setData(prev => ({ ...prev, [key]: event.target.value })); } function validator() { console.log(data); return new Promise((resolve, reject) => { // 修正正则为字面量写法,.增加转义 const isEmailValid = /\w+@example\.com/.test(data.email); let errMsg = ''; if (!isEmailValid) { errMsg = 'Email Not Valid! Please use Institute Email ID'; } if (data.password !== data.password2) { errMsg = 'Password Does Not Match!'; } // 直接用内部临时变量判断,不依赖异步更新的state if (errMsg !== '') { setError(errMsg); reject(errMsg); } else { resolve('ok'); } }); } const onSignup = (e) => { e.preventDefault(); validator() .then(() => { console.log('API Request Sent'); }) .catch((err) => { // 直接使用catch返回的错误信息,不需要依赖state的error console.log('The errors are: '+ err) }); }; return ( <form className={classes.form}> <TextField required className={classes.field} id="outlined-required" label="User Name" variant="outlined" onChange={(event) => updateData(event, 'username')} /> <TextField required type="email" className={classes.field} id="outlined-required" label="Email" variant="outlined" onChange={(event) => { updateData(event, 'email'); }} /> <TextField required className={classes.field} id="outlined-required" label="First Name" variant="outlined" onChange={(event) => updateData(event, 'first_name')} /> <TextField required className={classes.field} id="outlined-required" label="Last Name" variant="outlined" onChange={(event) => updateData(event, 'last_name')} /> <TextField required type="password" className={classes.field} id="outlined-required" label="Password" variant="outlined" onChange={(event) => updateData(event, 'password')} /> <TextField required type="password" className={classes.field} id="outlined-required" label="Confirm Password" variant="outlined" onChange={(event) => { updateData(event, 'password2'); }} /> <Button className={classes.field} color="primary" variant="contained" onClick={(e) => onSignup(e)} > Sign Up </Button> </form> ); }
内容的提问来源于stack exchange,提问作者Santam
相关产品推荐
相关产品推荐

