输入框不失焦导致数据不更新问题
总结:
-
核心是【React 合成事件 VS 原生事件差异】的实践场景
- Antd 为了防抖等性能问题,给 Input 组件的 change 事件触发时机是 onBlur,更新后,会把值同步给 form -> 这个同步过程,它既不是宏任务,也不是微任务,是通过 React 的 合成事件的状态更新机制 完成的
- 是异步的,并且遵循 React 的 useState 的更新规则,在 当前事件循环结束后 批量更新状态/统一处理状态变更(也就是 onBlur 后更新 form 的值) VS 原生 input 是同步的【这里的知识点就是 合成事件和原生事件的差异】
- 是 onBlur 还是 onChange 触发刷新并不重要,重要的是,触发 Input 值变更后,值的变更是异步的,依旧需要等待 当前事件循环结束后
- 执行顺序:
Sync code
→Micro task
→Macro task
→ 状态更新(React 批处理)
问题描述
- 不失焦时点【确定】按钮,form.getFieldsValue() 获取到的表单数据没有更新
原因排查
- InputNumber 在 antd 中做了优化,为了防抖,是在 onblur 触发事件 - 更新数据
解决方案
- 在 提交函数中 利用 setTimeout 将所有保存操作都 delay 250ms 实现【利用事件循环】
方案测试
1、强提交机制 +异步验证
将输入框的值更新流程更替为 :用户输入 → 等待blur → 通知Form更新 → 触发onValuesChange
强提交机制
step1:通过 ref 获取 InputNumber 实例
const inputRef = useRef();
<InputNumber ref={inputRef} />
step2:保存时手动触发 blur() 使组件提交未完成的输入
const handleSave = () => {
// 1. 手动触发 blur 确保 InputNumber 提交值
inputRef.current?.blur();
// 2. 稍等 10ms 让表单完成更新
setTimeout(() => {
form.validateFields().then(values => {
console.log('最终保存的值:', values.number);
// 这里执行保存逻辑...
});
}, 250);
};
异步验证:使用 setTimeout 短暂延迟,确保表单完成更新/或者promise 实现 sleep
-
出现问题:页面崩溃 Uncaught Error: Iframe has not been created yet
-
问题原因:当使用 ref 强制触发 blur() 时,如果组件尚未完成挂载(如 iframe 未初始化 - 业务是在弹窗上),直接操作 ref.current 可能会导致此错误
-
解决方案:防御性检查
if ( inputRef.current && // 1. ref 已绑定 inputRef.current.input && // 2. InputNumber 内部 input 已渲染 document.contains(inputRef.current.input) // 3. 组件仍在 DOM 中 ) { inputRef.current.blur(); await new Promise(resolve => setTimeout(resolve, 10)); }
-
新问题:时序问题,页面崩溃解决后,发现 blur 的确会触发 onChange 事件来更新数据,但是即使有 setTimeout 还是先进行了 数据的提交,再更新了数据 - 与预期的先后顺序不符合
核心问题总结:不需要这么麻烦,去获取 ref 来强制更新,只要知道 合成事件的批量更新机制,是在一个事件循环结束后,就可以利用 setTimeout 在下个事件循环获取最新、准确的数据
2、最终解决方案
将 onsubmit 的所有操作都放在 setTimeout(()=>{}, 250)
- 实际上是利用了 JavaScript 的事件循环机制,延迟了代码的执行,从而给 InputNumber 的 onBlur 或 onChange 事件足够的时间完成值的同步
- 注意知识点:Ant Design 的 InputNumber 的值同步并不是微任务,也不是宏任务,而是通过 React 的状态更新机制完成的。React 的状态更新机制是异步的,具体来说,它会在当前事件循环结束后批量更新状态。这种异步更新机制可能会导致在某些情况下,表单的值还没有同步到 Form 的内部状态中
- 注意知识点:当用户在 InputNumber 中输入值时,onChange 或 onBlur 会触发状态更新,但这个更新不会立即生效,而是会在当前事件循环结束后统一处理
执行顺序
1. 用户输入值。
2. 用户点击保存按钮,触发 onSubmit。
3. InputNumber 的 onBlur 事件触发,开始异步更新值。
4. onSubmit 中的 setTimeout 将保存逻辑推迟到 250ms 后。
5. onBlur 的值更新完成,Form 的内部状态同步了最新值。
6. setTimeout 的回调函数执行,此时 form.getFieldsValue() 获取到最新的值。