React+Formik注册表单用户名预校验状态更新异常问题
问题分析与修复方案
首先看你代码里的核心问题:
- axios响应状态码判断错误:axios的
response.status本身就是状态码数字(比如200、409),你写的response.status.code是不存在的,这导致请求成功时setUserError(false)根本不会执行,所以userError一直停留在"available"。 - 潜在的请求竞态问题:快速输入时,多个校验请求会同时发出,若旧请求比新请求晚返回,会覆盖正确的状态。
修复步骤
1. 修正axios状态码判断逻辑
把checkAvailabilty函数里的状态码判断改成正确写法:
const checkAvailabilty = async(e) =>{ try{ let response = await axios.get(`${state.baseUrl}users/is_available/${e.target.value}`) // 直接用response.status判断,而非response.status.code if(response.status === 200) { setUserError(false) } } catch(error){ // 先判断error.response是否存在,避免请求失败无响应的情况 if(error.response?.status === 409) { setUserError("用户名已存在") } else { setUserError("校验用户名失败,请稍后重试") } } }
2. 解决请求竞态问题
用axios的CancelToken取消未完成的旧请求,避免状态被覆盖:
// 初始化取消令牌源 let cancelTokenSource = null; const checkAvailabilty = async(e) =>{ // 先取消之前未完成的请求 if(cancelTokenSource) { cancelTokenSource.cancel("用户输入新内容,取消旧请求") } // 创建新的取消令牌 cancelTokenSource = axios.CancelToken.source() try{ let response = await axios.get(`${state.baseUrl}users/is_available/${e.target.value}`, { cancelToken: cancelTokenSource.token }) if(response.status === 200) { setUserError(false) } } catch(error){ // 忽略取消请求的错误 if(axios.isCancel(error)) return if(error.response?.status === 409) { setUserError("用户名已存在") } else { setUserError("校验用户名失败,请稍后重试") } } finally { // 请求完成后清空令牌源 if(!axios.isCancel(error)) { cancelTokenSource = null } } }
3. 推荐:整合到Formik的校验机制(更规范)
单独维护userError会和Formik自身的errors体系分离,建议把用户名校验整合到Formik的自定义校验中,复用Formik的错误处理:
<Formik initialValues={{ userName: '' }} validate={async(values) => { const errors = {} if(!values.userName) { errors.userName = '请输入用户名' } else { try { const response = await axios.get(`${state.baseUrl}users/is_available/${values.userName}`) if(response.status !== 200) { errors.userName = '用户名已存在' } } catch(error) { if(error.response?.status === 409) { errors.userName = '用户名已存在' } else { errors.userName = '校验用户名失败,请稍后重试' } } } return errors }} onSubmit={(values) => { // 提交逻辑 }} > {({ handleChange, handleBlur, values, errors, touched }) => ( <FormControl> <TextField name="userName" onBlur={handleBlur} value={values.userName} onChange={handleChange} error={touched.userName && Boolean(errors.userName) } helperText={touched.userName && errors.userName } /> </FormControl> )} </Formik>
内容的提问来源于stack exchange,提问作者Asra Fatima
相关产品推荐
相关产品推荐

