如何在React中同步调用Axios?解决MongoDB用户重复注册问题
解决React中Axios异步验证导致重复注册的问题
你的问题核心在于**validateRegister是同步函数,但里面的用户存在性校验是异步的Axios请求**——函数会在请求还没拿到响应时就返回初始的errors对象,导致后续注册逻辑误以为没有重复用户,直接执行了注册操作。下面是具体的解决方案和修改后的代码:
步骤1:将验证函数改为异步函数
把validateRegister改成async函数,用await等待Axios请求完成,确保拿到结果后再返回完整的错误信息:
const validateRegister = async (values) => { let errors = {}; const patternName = new RegExp('^[a-zA-Z ]{3,20}$') const patternPhone = new RegExp('^[0-9]{9,10}$') const patternEmail = new RegExp('^[a-zA-Z0-9._:$!%-]+@[a-zA-Z0-9.-]+.[a-zA-Z]$') const patternPassword = new RegExp('(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[^A-Za-z0-9])(?=.{8,})') // 先完成同步格式校验 if(!values.name || !patternName.test(values.name)){ errors.name="Please enter a valid name" } if(!values.phone || !patternPhone.test(values.phone)){ errors.phone="Please enter a valid Phone number of 9-10 digits" } if(!values.email || !patternEmail.test(values.email)){ errors.email="Please enter a valid email address" } if(!values.password || !patternPassword.test(values.password)){ errors.password="Please enter a strong password to continue. A strong password has: Atleast 8 characters in length, 2 letters in upper case, 1 special character (!@#$&*), 2 number (0-9), 3 letters in lower case" } if(!values.isTermsAndConditionsAccepted){ errors.isTermsAndConditionsAccepted = "Please Accept the Terms and conditions" } // 如果格式校验已有错误,直接返回,无需查询数据库 if (Object.keys(errors).length > 0) { return errors; } // 异步校验用户是否存在 if(values.phone){ let formData = new FormData(); formData.append('phone', values.phone); console.log('inside user check') try { // 等待请求响应完成 await axios.post('http://localhost:3001/doesUserExistByPhone', formData); // 请求成功说明用户不存在,无需添加错误 } catch (errorResponse) { console.log(errorResponse) if(errorResponse.response.status===409){ console.log('User already exist'); errors.phone="User Already exist. If you've already registered. Please try to Login."; } else if(errorResponse.response.status===500){ errors.phone = "Unable to register user, contact SwapiFi Support"; } } } console.log('Errors found before creating user: ', errors); return errors; } export default validateRegister
步骤2:修改调用验证函数的逻辑
在useFormRegister里,因为validateRegister现在是异步函数,所以handleRegisterEvent要改成async函数,用await获取验证结果后再设置errors:
const useFormRegister = (submitForm) => { const [errors, setErrors] = useState({}); const [dataIsCorrect, setDataIsCorrect] = useState(false); const [values, setValues] = useState({ name: "", phone: "", email: "", password: "", isTermsAndConditionsAccepted: false }) const [isLoading, setIsLoading] = useState(false); // 新增loading状态,防止重复点击 const handleValueChangeEvent = (event) => { setValues({ ...values, [event.target.name] : event.target.value, }) } const handleRegisterEvent = async (event) => { console.log('Register button clicked'); event.preventDefault(); setIsLoading(true); // 开始验证时锁定按钮 // 等待验证完成,拿到完整错误信息 const validationErrors = await validateRegister(values); setErrors(validationErrors); setIsLoading(false); // 验证完成后解锁按钮 setDataIsCorrect(true); } useEffect(() => { console.log('No. of errors:', Object.keys(errors).length) Object.entries(errors).map(([key, value]) => ( console.log("Error, ", key, ':', value) )) if(Object.keys(errors).length === 0 && dataIsCorrect){ submitForm(true); let formData = new FormData(); Object.entries(values).map(([key, value]) => ( formData.append(key, value) )) console.log(formData) axios.post('http://localhost:3001/registerUser', formData) .then(response => {console.log(response)}) .catch(error=>{console.log(error)}) // 注册完成后重置状态 setDataIsCorrect(false); } }, [errors, dataIsCorrect, values, submitForm]) return {handleValueChangeEvent, handleRegisterEvent, values, errors, isLoading}; } export default useFormRegister
额外注意事项
- 后端必须重复校验:前端校验只是提升用户体验,恶意用户可以绕过前端直接调用后端接口,所以
registerUser接口必须再次检查用户是否存在,返回409错误。 - 利用loading状态防重复提交:上面的代码新增了
isLoading状态,在验证和注册过程中可以禁用注册按钮,避免用户重复点击导致多次请求。 - 确认后端状态码逻辑:要确保
doesUserExistByPhone接口的状态码逻辑正确——比如用户存在时返回409,不存在时返回200,这样前端的try/catch才能正确处理。
内容的提问来源于stack exchange,提问作者ChivalrousRoot
相关产品推荐
相关产品推荐

