输入框不失焦导致数据不更新问题

Posted by CodingWithAlice on April 13, 2025

输入框不失焦导致数据不更新问题

总结:

  • 核心是【React 合成事件 VS 原生事件差异】的实践场景

  • Antd 为了防抖等性能问题,给 Input 组件的 change 事件触发时机是 onBlur,更新后,会把值同步给 form -> 这个同步过程,它既不是宏任务,也不是微任务,是通过 React 的 合成事件的状态更新机制 完成的
  • 是异步的,并且遵循 React 的 useState 的更新规则,在 当前事件循环结束后 批量更新状态/统一处理状态变更(也就是 onBlur 后更新 form 的值) VS 原生 input 是同步的【这里的知识点就是 合成事件和原生事件的差异】
  • 是 onBlur 还是 onChange 触发刷新并不重要,重要的是,触发 Input 值变更后,值的变更是异步的,依旧需要等待 当前事件循环结束后
  • 执行顺序: Sync codeMicro taskMacro 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() 获取到最新的值