破解 React 受控组件:如何从外部脚本修改 Input 值?

21 阅读2分钟

⚛️ 破解 React 受控组件:如何从外部脚本修改 Input 值?

摘要:在开发浏览器插件或自动化脚本时,你是否遇到过“修改了 input.value 但 React 状态没更新”的尴尬?本文教你一招破解 React 受控组件的限制。

问题复现

假设有一个 React 组件:

function App() {
  const [val, setVal] = useState("");
  return <input value={val} onChange={e => setVal(e.target.value)} />;
}

如果你在控制台运行:

document.querySelector('input').value = 'hello';

你会发现输入框里确实显示了 'hello',但是一旦你点击输入框,它又变回空了! 这是因为 React 的 state 并没有更新,重新渲染时又把 value 覆盖回去了。

原理分析

React 在底层劫持了 input 元素的 value setter。当你直接赋值时,React 内部的逻辑被绕过了。 而且,React 依赖 input 事件来更新 state。

AutoForm 的解决方案

在开发 AutoForm 智能填充 SDK 时,我们必须解决这个问题,否则无法支持 React 开发的系统。

我们需要做两件事:

  1. 调用原生 Setter:绕过 React 的劫持。
  2. 触发 Input 事件:通知 React 更新状态。

核心代码

function setNativeValue(element: HTMLInputElement, value: string) {
  // 1. 获取原生的 value setter
  const valueSetter = Object.getOwnPropertyDescriptor(element, 'value')?.set;
  const prototype = Object.getPrototypeOf(element);
  const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;

  // 2. 调用原生 setter
  if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    valueSetter?.call(element, value);
  }

  // 3. 触发 input 事件
  element.dispatchEvent(new Event('input', { bubbles: true }));
}

进阶:处理复杂组件

对于 Ant Design 的 SelectDatePicker,它们往往由多个 DOM 元素组成(隐藏的 input + 模拟的 UI)。 这时候仅仅修改 input 是不够的。

AutoForm 的策略是:模拟真实用户行为。 我们集成了 @testing-library/user-event,通过模拟 click -> type -> enter 的完整流程,让组件“以为”是真人在操作。

结语

前端自动化不仅仅是简单的 DOM 操作,更需要对框架原理有深入理解。 希望本文对你有所启发。


👉 官网地址:51bpms.com

也可以添加微信详细沟通

linkme.png