React Custom Hook返回error状态值始终滞后一步问题修复
问题原因
错误提示滞后是React状态更新的闭包+异步更新特性导致的:setError触发的状态更新不会同步生效,调用setError后,当前执行上下文中获取的error仍然是上一次渲染产生的闭包旧值,所以永远会比实际校验结果慢一次。
原代码还附带几个影响逻辑的小问题:
- 遍历数组误用
for...in语法,该语法遍历的是对象可枚举键,用在数组上拿到的是索引而非实际值 validate函数校验全量通过时没有显式返回true,会默认返回undefined被判断为校验失败- 错误日志拼写错误,
err.mesage少写了一个s - 校验标记
valid定义在useValidation顶层,多次调用校验会出现值污染
修复方案
不要依赖状态中存储的error获取当次校验结果,直接在校验逻辑中将错误信息和校验状态同步返回,绕开闭包拿旧值的问题。
修复后完整代码:
const AddSomething = () => { const [error, validate] = useValidation() const handleSave = async () => { // 直接接收校验函数返回的实时结果,不依赖闭包中的state值 const { valid, errorMsg } = await validate(valuesToBeSaved) if(valid) { saveItems() } else { console.log("ELSE") setSnackBarError({ message: errorMsg }) } } } function useValidation() { const [error, setError] = useState('') async function validate(valuesToBeSaved: string[] | number[]) { let valid = true let currentError = '' // 修正数组遍历语法 for(const value of valuesToBeSaved) { if(typeof value === 'string'){ const res = await validateName(value) valid = res.valid currentError = res.errorMsg } if(!valid) break } // 校验完成后统一更新状态,同时返回实时结果 setError(currentError) return { valid, errorMsg: currentError } } async function validateName(name: string): Promise<{valid: boolean, errorMsg: string}> { console.log("inside validateName") try { await nameSchema.validate({ name }) return { valid: true, errorMsg: '' } } catch(err) { console.log("error is set to: " + err.message) return { valid: false, errorMsg: err.message } } } return [error, validate] } const nameSchema = yup.object({ name: yup .string() .min(1, 'Name can not be below 1') .max(50, 'Name can not be above 50') .required('Name is required'), })
修复说明
- 校验过程中产生的错误信息存在函数局部变量中,不依赖state的实时值
- 单字段校验方法直接返回校验状态和对应错误信息,不需要等待状态更新
- 所有校验逻辑执行完成后再统一更新error状态,同时将当次校验的实时结果返回给调用方,从根源避免状态滞后问题
- 同步修复了原代码中遍历语法、返回值、拼写、变量污染的附带问题
内容的提问来源于stack exchange,提问作者JamesG
相关产品推荐
相关产品推荐

