在 React 中模拟输入

需求 与 Bug

项目的 C# 桌面端使用 CefSharp 内嵌了一个三方网站,在外部实现了一个登录控件,外部登录后希望内嵌的三方网站自动登录,实现代码如下:

browser.ExecuteScriptAsync($"document.getElementsByName('username')[0].value = '{_login}'");
browser.ExecuteScriptAsync($"document.getElementsByName('password')[0].value = '{_pwd}'");
browser.ExecuteScriptAsync($"document.getElementsById('submitBtn').click()");

通过 CefSharp 提供的方法在外部执行脚本,将登录控件的值填充至网站,旧版中正常,三方网站改版后无法正常填充。

尝试解决

直接修改 input 的 value 值无效,通过控制台 sources 面板查看,发现网站用 React 重构:

Image

React 是状态驱动的视图库,直接修改元素的 value 值不会导致 React 应用状态的改变,我们需要让 React 感知到数据变化,即事件模拟:

const inputEl = document.querySelector('input');
inputEl.value = "changed";
inputEl.dispatchEvent(new Event('input', { bubbles: true, cancelable: false, composed: true }));

结果是页面显示已经改变:

Image

但内部的状态依然未变:

Image

查询资料找到一个 React Issues,有人在讨论中给出解决方案 Trigger simulated input value,代码如下:

let input = someInput; 
let lastValue = input.value;
input.value = 'new value';
let event = new Event('input', { bubbles: true });
// hack React15
event.simulated = true;
// hack React16 内部定义了 descriptor 拦截 value,此处重置状态
let tracker = input._valueTracker;
if (tracker) {tracker.setValue(lastValue);
}
input.dispatchEvent(event);

问题解决。

知其然而知其所以然

上述问题激起了个人好奇心,尝试通过调试理解模拟输入事件无效的原因。

通过控制台 -> Elements -> Event Listeners 面板,找到 input 事件,查看已注册的事件处理程序:

Image

React 实现了自己的合成事件系统,几乎所有的事件统一注册在 React 应用根元素上,这里即是截图中的 root 元素,点击右侧链接定位至源码:

Image

所有事件都经过此函数,避免时间浪费,我们加上条件,只有 input 事件才会断点:

Image

输入触发断点,中间会经过很多我们不关心的流程,最终进入 updateValueIfChanged 函数:

function getTracker(node) {return node._valueTracker;
}function getValueFromNode(node) {var value = '';if (!node) {return value;}if (isCheckable(node)) {value = node.checked ? 'true' : 'false';} else {value = node.value;}return value;
}function updateValueIfChanged(node) {if (!node) {return false;}// 获取 trackervar tracker = getTracker(node);if (!tracker) {return true;}// 获取上次的值var lastValue = tracker.getValue();// 获取变更值var nextValue = getValueFromNode(node);// 不同则更新,此处是关键节点if (nextValue !== lastValue) {tracker.setValue(nextValue);return true;}return false;
}

tracker 的定义如下:

function trackValueOnNode(node) {// 判断 input 类型var valueField = isCheckable(node) ? 'checked' : 'value';// 定义属性描述符var descriptor = Object.getOwnPropertyDescriptor(node.constructor.prototype, valueField);// 设置初始值var currentValue = '' + node[valueField];if (node.hasOwnProperty(valueField) ||typeof descriptor === 'undefined' ||typeof descriptor.get !== 'function' ||typeof descriptor.set !== 'function') {return;}var get = descriptor.get,set = descriptor.set;// 对 value 属性的获取与设置进行拦截Object.defineProperty(node, valueField, {configurable: true,get: function () {return get.call(this);},set: function (value) {{checkFormFieldValueStringCoercion(value);}/*** 注意此处,每当设置元素的 value 属性时,currentValue 也被修改,* 导致 updateValueIfChanged 函数始终返回 false*/currentValue = '' + value;set.call(this, value);}});Object.defineProperty(node, valueField, {enumerable: descriptor.enumerable});var tracker = {getValue: function () {return currentValue;},setValue: function (value) {{checkFormFieldValueStringCoercion(value);}currentValue = '' + value;},stopTracking: function () {detachTracker(node);delete node[valueField];}};return tracker;
}

我们来看看真实输入的流程:

  1. 触发输入
  2. 进入 updateValueIfChanged
  3. 获取旧值和新值
  4. 两者不同,设置 value,updateValueIfChanged 返回 true(重要)

再来看看模拟的输入流程:

  1. input.value = ‘changed’
  2. 触发 value 属性的拦截,将内部的 tracker currentValue 更新为 changed
  3. 触发输入事件
  4. 进入 updateValueIfChanged
  5. 获取旧值和新值
  6. 两者相同,updateValueIfChanged 返回 false(重要)

这里模拟输入时,新旧值是相同的,所以 updateValueIfChanged 函数会返回 false,当返回 false 时不会触发 React 的更新机制去更新状态,即无法进入下图的流程:

Image

我们再来看看之前的解决方案:

// 暂存旧值
let lastValue = input.value;// 设置新值
input.value = 'new value';// ...// 获取 tracker
let tracker = input._valueTracker;if (tracker) {// 将 tracker 设置为 旧值,此时新旧值不同,updateValueIfChanged 会返回 truetracker.setValue(lastValue);
}// ...

—end

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1539150.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Etcd权限认证管理

1 查看是否开启权限认证 ctl auth status 2 开启权限认证 ctl auth enable。开启后每一条命令都要加上用户 --userroot:root(root默认最高权限) 3 创建其他用户 ctl user add user1 --user用户名:密码 4 创建角色 ctl role add testR --user 5 为角色添加权限 ctl role g…

Linux基础命令——文件系统的日常管理

目录 一.如何查看当前工作目录?(你现在所处的位置路径) 二.命令touch的用途是什么?还有别的方法新建文件吗? (1)创建空文件 (2)如果已经存在这个文件,就会更新创建时间。 (3…

优化器与现有网络模型的修改

文章目录 一、优化器是什么二、优化器的使用三、分类模型VGG16四、现有网络模型的修改 一、优化器是什么 优化器(Optimizer)是一个算法,用于在训练过程中调整模型的参数,以便最小化损失函数(Loss Function&#xff09…

【论文阅读笔记】YOLOv10: Real-Time End-to-End Object Detection

论文地址:https://arxiv.org/abs/2405.14458 文章目录 论文小结论文简介论文方法为NMS-free训练的一致性双标签分配双标签分配一致性匹配度量 效率-精度整体驱动的模型设计效率驱动模型设计轻量级分类检测头Spatial-channel 解耦下采样Rank-guided block design 精度…

linux 操作系统下的dhclient命令介绍和案例使用

linux 操作系统下的dhclient命令介绍和案例使用 dhclient 是 Linux 系统中用于动态主机配置协议(DHCP)客户端的命令。它的主要功能是从 DHCP 服务器获取网络配置,包括 IP 地址、子网掩码、默认网关和 DNS 服务器等信息 dhclient 命令概述 …

transformer共享权重对联模型

嵌入维度512,8头,1层 |分割中最从左到右依次是数据集上联,模型预测下联,数据集下联 ,有些对联对的还是可以的 嵌入维度512,8头,3层,最后一个输出层采用线性层,模型训练过程 上面是模型训练过程,下面是模型训练结果 从左到右,上联,模型生成,下…

满足10人同时绘图的图形工作站

在当今这个数字化与创意并重的时代,图形工作站作为设计师、艺术家及数字内容 创作者们的重要工具,其性能与效率直接关系到项目的成功与否。 当谈及满足10人同时绘图的图形工作站时,我们不仅要考虑硬件的峰值性能,还需兼顾软件的兼…

PSINS,GNSS速度与SINS滤波的MATLAB代码

文章目录 程序说明主要特点适用范围获取方式运行截图 程序说明 基于PSINS工具箱的GNSS和SINS滤波的MATLAB代码,观测量为GNSS的三轴速度。 专为工程师和研究人员设计,助您轻松实现高精度的导航和定位。 主要特点 高精度滤波算法:结合PSINS和…

中间件:maxwell、canal

文章目录 1、底层原理:基于mysql的bin log日志实现的:把自己伪装成slave2、bin log 日志有三种模式:2.1、statement模式:2.2、row模式:2.3、mixed模式: 3、maxwell只支持 row 模式:4、maxwell介…

思通数科开源智能文档识别平台的核心功能

思通数科的智能文档识别平台是一个综合性的解决方案,旨在通过人工智能技术提升文档识别处理的效率和准确性。 主要的功能是: 1. 信息抽取与数据结构化 票据识别与抽取:利用OCR技术自动识别和提取票据上的关键信息,如日期、金额等…

几何 | 数学专项

日期内容2024.9.19创建 { d > 0 , 递增数列 d < 0 , 递减数列 d 0 &#xff0c;常数列 \begin{cases} d>0,递增数列\\ d<0,递减数列\\ d0&#xff0c;常数列 \end{cases} ⎩ ⎨ ⎧​d>0,递增数列d<0,递减数列d0&#xff0c;常数列​ 【2010.13】 【1.历年真…

三菱变频器以模拟方式进行频率设定:(电压输入)

POINT 在STF(STR)信号 ON时&#xff0c;发出启动指令。 通过电位器(频率设定器)下达频率指令。(端子2-5之间连接(电压输入)) 接线示例 (变频器为频率设定器提供5V 电源。(端子 10)) 操作示例 以 60Hz 运行 NOTE. 1、正转开关(STF)与反转开关(STR)同时为0N时无法启动。此外&a…

利用Leaflet.js和turf.js创建交互式地图:航道路线绘制

引言 在现代Web应用中&#xff0c;地图的交互性是提供丰富用户体验的关键。Leaflet.js是一个轻量级的开源JavaScript库&#xff0c;它提供了简单易用的API来构建交云的地图。与此同时&#xff0c;turf.js作为一个强大的地理空间分析库&#xff0c;能够处理复杂的地理数据操作。…

SourceTree保姆级教程1:(克隆,提交,推送)

本人认为sourceTree 是最好用的版本管理工具&#xff0c;下面将讲解下sourceTree 客户端工具 克隆&#xff0c;提交&#xff0c;推送 具体使用过程&#xff0c;废话不多说直接上图。 使用步骤&#xff1a; 首先必须要先安装Git和sourceTree&#xff0c;如何按照参考其它文章&…

C++第四讲:模板初阶

C第四讲&#xff1a;模板初阶 1.泛型编程2.函数模板2.1什么是函数模板2.2函数模板的使用2.3函数模板的原理2.4函数模板的实例化2.4.1隐式实例化2.4.2显式实例化 2.5模板参数的匹配原则2.5.1原则12.5.2原则22.5.3原则3 3.类模板3.1类模板的定义格式3.2类模板的实例化 1.泛型编程…

GPT代码记录

#include <iostream>// 基类模板 template<typename T> class Base { public:void func() {std::cout << "Base function" << std::endl;} };// 特化的子类 template<typename T> class Derived : public Base<T> { public:void…

在线安全干货|如何更改IP地址?

更改IP地址是一个常见的需求&#xff0c;无论是为了保护个人隐私、绕过地理限制还是进行商业数据分析。不同的IP更改方法适用于不同的需求和环境。但请注意&#xff0c;更改IP地址应在合法场景下进行&#xff0c;无论使用什么方法&#xff0c;都需要在符合当地网络安全法律法规…

寻呼机爆炸,炸醒通讯安全警惕心

据央视新闻报道&#xff1a;当地时间17日下午&#xff0c;黎巴嫩首都贝鲁特以及黎巴嫩东南部和东北部多地发生寻呼机爆炸事件。黎巴嫩公共卫生部长阿卜亚德称&#xff0c;爆炸已造成9人死亡&#xff0c;约有2800人受伤&#xff0c;其中约200人伤情危重。 来源&#xff1a;央视新…

20240919在友善之臂的NanoPC-T6开发板上适配宸芯的数传模块CX6602N

20240919在友善之臂的NanoPC-T6开发板上适配宸芯的数传模块CX6602N 2024/9/19 16:54 缘起&#xff0c;大毛PK二毛战况激烈&#xff0c;穿越机大卖&#xff01;我司拆同行的图传作品。 发现&#xff1a; 主控&#xff1a;飞凌OK3588-C核心板 图传模块&#xff1a;宸芯的数传模块…