React输入首字符时state存空值 表单校验失效问题如何解决
问题原因
React useState的状态更新是异步批处理机制:调用setModalReasonInput(value)时,当前执行上下文中的modalReasonInput不会立刻变更,必须等当前同步逻辑执行完成、组件触发重渲染后,新的状态值才会在新的闭包中生效。
原代码在handleChange里调用setModalReasonInput后立刻执行validateReason,此时校验函数读到的永远是上一次渲染缓存的旧状态值,因此会出现状态更新慢一拍、首次输入校验失效的问题。
可行解决方案
方案1:直接传入最新值做校验(优先推荐)
不需要等待状态更新,直接把输入框拿到的最新值传入校验函数,完全绕开闭包旧值问题,逻辑最直白、性能最好:
const [modalReasonInput, setModalReasonInput] = useState("") const [error, setError] = useState("") // 校验函数接收待校验的值作为入参,不直接依赖闭包中的state const validateReason = (currentValue) => { console.log(currentValue) if (currentValue === "" && record.judgement_result === "fail") { setError("Field is required") return false } setError("") return true } const handleChange = (value) => { setModalReasonInput(value) // 直接传入最新输入值校验 validateReason(value) } return ( <TextField type="textarea" maxLength={1000} showCounting rows={6} value={modalReasonInput} // 失焦时state已经完成更新,直接传当前state值即可 onBlur={() => validateReason(modalReasonInput)} onChange={(e) => handleChange(e.target.value)} /> )
方案2:通过useEffect监听状态变化自动校验
如果不想修改校验函数的入参,可以借助useEffect,等状态更新完成、组件重渲染后自动触发校验逻辑:
const [modalReasonInput, setModalReasonInput] = useState("") const [error, setError] = useState("") const validateReason = () => { console.log(modalReasonInput) if (modalReasonInput === "" && record.judgement_result === "fail") { setError("Field is required") return false } setError("") return true } // 监听输入值变化,状态更新后自动执行校验 useEffect(() => { validateReason() // 注意把校验逻辑依赖的所有变量都加到依赖数组中 }, [modalReasonInput, record.judgement_result]) const handleChange = (value) => { setModalReasonInput(value) // 不需要手动调用校验,状态变化后useEffect会自动触发 } return ( <TextField type="textarea" maxLength={1000} showCounting rows={6} value={modalReasonInput} onBlur={() => validateReason()} onChange={(e) => handleChange(e.target.value)} /> )
补充说明:不要试图用
flushSync强制同步更新state解决这个问题,会破坏React的批处理优化,大幅降低页面性能,除非有极特殊的场景需求,否则不推荐使用。
内容的提问来源于stack exchange,提问作者psudo
相关产品推荐
相关产品推荐

