最近碰到一个小难题,针对某个页面,提前植入js执行以填充某个Dom元素的值,例如需要填充某些表单便于用户进行一键提交。如果是原生的页面,使用element.value赋值就可以实现,比较简单,但是如果是框架实现的页面,例如React框架,就会遇到一个困难,就是你修改完了Dom元素的值再去触发了React框架重新渲染就会让React框架自身的state输出到Dom元素中,导致修改的值被回滚了。
打上断点就会发现回滚操作
实际上回滚的本质原因,就是因为没有同步修改React框架内保存的状态,导致不一致被React框架给回滚了。
那如何修改React框架内保存的状态呢?
这里本质上,是需要解决React的state需要同步进行更改,如果只是修改Dom元素,就迟早会被React回滚回来。
解决前需要了解一下React框架的 _valueTracker
是什么
_valueTracker
是 React 内部用于跟踪和管理表单元素(如 <input>
、<textarea>
等)值变化的一个对象。它是 React 早期版本(特别是 React 15 及之前版本)中实现受控组件(controlled components)的一种方式。
在 React 中,受控组件是指其值由 React 组件的状态控制的表单元素。这意味着表单元素的当前值存储在组件的状态中,并且任何更改都必须通过更新状态来驱动。这种方式使得 React 可以完全控制表单元素的行为和渲染。
_valueTracker
的作用_valueTracker对象用于记录表单元素的当前值以及该值是否由用户直接输入引起。它主要有两个属性:
value
:存储表单元素的当前值。
isMounted
:表示组件是否已经挂载到 DOM 上。
React 使用 _valueTracker
来优化性能和确保状态的一致性。例如,当组件的状态更新时,React 可以检查 _valueTracker
来确定是否需要重新渲染表单元素。
_valueTracker
的实现机制当一个受控组件被创建时,React 会在组件实例上初始化一个 _valueTracker 对象,并将其与表单元素的当前值关联起来。
当组件的状态更新时,React 会检查 _valueTracker 中存储的值与新的状态值是否一致。
如果不一致,React 会更新表单元素的 DOM 值,并触发相应的事件(如 input 事件)。
当用户直接在表单元素中输入内容时,React 会捕获这些事件,并更新 _valueTracker 中的值。
这种机制确保了 React 的状态始终与实际的 DOM 值保持同步。
_valueTracker 还用于优化性能。例如,当组件的状态更新时,React 可以检查 _valueTracker 来确定是否需要重新渲染表单元素。
如果 _valueTracker 中的值已经是最新的,React 可以避免不必要的 DOM 操作。
从 React 16 开始,React 引入了更先进的合成事件系统和更优化的状态管理机制,_valueTracker 不再是必需的,并且逐渐被废弃。在 React 17 及更高版本中,_valueTracker 已经被移除,React 使用其他方式来跟踪和管理表单元素的值。
了解完_valueTracker 可以知道,React17以下基本上是可以兼容的,所幸我们需要修改的页面正好是符合要求的版本,直接上解决的代码(内含注释)
function changeReactInputValue(inputDom, newText) {
let lastValue = inputDom.value; // 1. 获取输入框的当前值
inputDom.value = newText; // 2. 将输入框的值设置为新的文本
let event = new Event('input', { bubbles: true }); // 3. 创建一个新的 'input' 事件,并允许事件冒泡
event.simulated = true; // 4. 标记事件为模拟事件(非用户真实操作)
let tracker = inputDom._valueTracker; // 5. 访问React内部用于跟踪输入值的 _valueTracker 对象
if (tracker) { // 6. 如果存在 _valueTracker 对象
tracker.setValue(lastValue); // 7. 更新 _valueTracker 中的值,以保持React内部状态的一致性
}
inputDom.dispatchEvent(event); // 8. 触发 'input' 事件,通知React值已发生变化
}
// eg.
changeReactInputValue(document.getElementsByName("${className}")[0], "123123")
通过直接操作DOM来更改React管理的输入框的值,并通过手动触发事件和操作React的内部跟踪机制,确保React的状态与DOM保持同步。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。