jQuery时代,模拟表单输入很简单,本质上就是操作dom,选择对于的dom,给dom.value设置值即可。
到了react时代就不同了,虽然也可以通过js拿到dom,给dom.value设置,但是react的状态绑定下,单纯给dom设置value,可是不会直接让react对于的状态也变化的。举个例子:
<input onChange={e=>console.log(e)} />
这么写一个react组件,通过js得到这个dom后dom.value=123;并不会触发onChange事件。
之前遇到过这个问题,不过没有深究,刚好同事分享到automa,发现automa靠选择器也能模拟表单填写,于是再研究了一下。
1. 原生分发事件其实是有的
如下:
// html
<input id="ninput" />// js
var $dom = document.querySelector('#ninput');
$dom.addEventListener('change', e=>{console.log('trigger change', e, e.target.value);
})// manual js
var $dom = document.querySelector('#ninput');
$dom.value = "123";
$dom.dispatchEvent(new Event("change", {bubbles:true}));
利用new Event 和 dispatchEvent就可以触发绑定事件
2. dispatchEvent能否也用在react组件上呢?
既然上面可以,其实理论上,应该也是支持的,先试试下面代码
// jsx
<input onChange={e=>console.log(e)} />// manual js
var $dom = document.querySelector('#root input');
$dom.value = "123";
$dom.dispatchEvent(new Event("change", {bubbles:true}));
咦,怎么界面的input确实改成了123,但是没有console.log啊,也就是没有触发onChange
理论上,react上的事件虽然不是直接绑在dom,而是合成事件,但合成事件也是root上绑定的事件,等待冒泡上来的,new Event也设置了bubbles:true
后来在stackoverflow看到一个回答示例,改写了一下:
// jsx
<input onChange={e=>console.log(e)} />// manual js
var $dom = document.querySelector('#root input');const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set;nativeInputValueSetter.call($dom, '1234');const event = new Event('change', { bubbles: true });$dom.dispatchEvent(event);
用了另外一种方式给dom设置value
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
window.HTMLInputElement.prototype,
'value').set;
nativeInputValueSetter.call($dom, '1234');
触发了console.log,但是 如果重复设置同一个值,也是不会触发到onChange的。
参考:reactjs - What is the best way to trigger change or input event in react js - Stack Overflow