我的任务是抓取用 React 构建的网站。我正在尝试填写输入字段并使用 javascript 注入到页面(移动设备中的 selenium 或 webview)提交表单。这对其他所有网站+技术来说都是一种魅力,但 React 似乎是一个真正的痛苦。
所以这是一个示例代码
var email = document.getElementById( 'email' );
email.value = '[email protected]';
我 DOM 输入元素上的值发生变化,但 React 不会触发更改事件。
我一直在尝试多种不同的方法来让 React 更新状态。
var event = new Event('change', { bubbles: true });
email.dispatchEvent( event );
无济于事
var event = new Event('input', { bubbles: true });
email.dispatchEvent( event );
不工作
email.onChange( event );
不工作
我不敢相信与 React 交互变得如此困难。我将非常感谢任何帮助。
谢谢你
由于重复数据删除输入和更改事件的更改,此已接受的解决方案似乎不适用于 React > 15.6(包括 React 16)。
您可以在这里查看 React 讨论:https://github.com/facebook/react/issues/10135
这里建议的解决方法: https://github.com/facebook/react/issues/10135#issuecomment-314441175
为方便转载于此:
而不是
input.value = 'foo';
input.dispatchEvent(new Event('input', {bubbles: true}));
你会用
function setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
然后
setNativeValue(input, 'foo');
input.dispatchEvent(new Event('input', { bubbles: true }));
React 正在监听文本字段的
input
事件。
您可以更改值并手动触发输入事件,react 的
onChange
处理程序将触发:
class Form extends React.Component {
constructor(props) {
super(props)
this.state = {value: ''}
}
handleChange(e) {
this.setState({value: e.target.value})
console.log('State updated to ', e.target.value);
}
render() {
return (
<div>
<input
id='textfield'
value={this.state.value}
onChange={this.handleChange.bind(this)}
/>
<p>{this.state.value}</p>
</div>
)
}
}
ReactDOM.render(
<Form />,
document.getElementById('app')
)
document.getElementById('textfield').value = 'foo'
const event = new Event('input', { bubbles: true })
document.getElementById('textfield').dispatchEvent(event)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'></div>
这是输入、选择、复选框等最干净的解决方案(不仅适用于反应输入)
/**
* See [Modify React Component's State using jQuery/Plain Javascript from Chrome Extension](https://stackoverflow.com/q/41166005)
* See https://github.com/facebook/react/issues/11488#issuecomment-347775628
* See [How to programmatically fill input elements built with React?](https://stackoverflow.com/q/40894637)
* See https://github.com/facebook/react/issues/10135#issuecomment-401496776
*
* @param {HTMLInputElement | HTMLSelectElement} el
* @param {string} value
*/
function setNativeValue(el, value) {
const previousValue = el.value;
if (el.type === 'checkbox' || el.type === 'radio') {
if ((!!value && !el.checked) || (!!!value && el.checked)) {
el.click();
}
} else el.value = value;
const tracker = el._valueTracker;
if (tracker) {
tracker.setValue(previousValue);
}
// 'change' instead of 'input', see https://github.com/facebook/react/issues/11488#issuecomment-381590324
el.dispatchEvent(new Event('change', { bubbles: true }));
}
用途:
setNativeValue(document.getElementById('name'), 'Your name');
document.getElementById('radio').click(); // or setNativeValue(document.getElementById('radio'), true)
document.getElementById('checkbox').click(); // or setNativeValue(document.getElementById('checkbox'), true)
我注意到输入元素有一些名称类似于
__reactEventHandlers$...
的属性,它具有一些功能,包括 onChange
。
这有助于找到该功能并触发它
更新:
__reactEventHandlers$...
似乎已更名为 __reactProps$...
,所以请在下面的 .filter
中尝试一下
let getReactEventHandlers = (element) => {
// the name of the attribute changes, so we find it using a match.
// It's something like `element.__reactEventHandlers$...`
let reactEventHandlersName = Object.keys(element)
.filter(key => key.match('reactEventHandler'));
return element[reactEventHandlersName];
}
let triggerReactOnChangeEvent = (element) => {
let ev = new Event('change');
// workaround to set the event target, because `ev.target = element` doesn't work
Object.defineProperty(ev, 'target', {writable: false, value: element});
getReactEventHandlers(element).onChange(ev);
}
input.value = "some value";
triggerReactOnChangeEvent(input);
React 17 适用于纤维:
function findReact(dom) {
let key = Object.keys(dom).find(key => key.startsWith("__reactFiber$"));
let internalInstance = dom[key];
if (internalInstance == null) return "internalInstance is null: " + key;
if (internalInstance.return) { // react 16+
return internalInstance._debugOwner
? internalInstance._debugOwner.stateNode
: internalInstance.return.stateNode;
} else { // react <16
return internalInstance._currentElement._owner._instance;
}
}
然后:
findReact(domElement).onChangeWrapper("New value");
其中的 domElement 是带有您要更改的字段的数据参数名称的 tr:
var domElement = ?.querySelectorAll('tr[data-param-name="<my field name>"]')
没有元素 ID:
export default function SomeComponent() {
const inputRef = useRef();
const [address, setAddress] = useState("");
const onAddressChange = (e) => {
setAddress(e.target.value);
}
const setAddressProgrammatically = (newValue) => {
const event = new Event('change', { bubbles: true });
const input = inputRef.current;
if (input) {
setAddress(newValue);
input.value = newValue;
input.dispatchEvent(event);
}
}
return (
...
<input ref={inputRef} type="text" value={address} onChange={onAddressChange}/>
...
);
}
这对我不起作用。我查看了源代码,有一个
document.activeElement
检查阻碍了我。
所以,我在代码中添加了element.focus();
,它开始工作了。
element.focus();
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
element.dispatchEvent(new Event('input', { bubbles: true }));