9

I'm trying to make a Tampermonkey script that'll automatically enter text into some form input fields.

Normally, you can do this with just:

myElement.value = "my new text"

Problem is, this form is using React, and I can't directly change the value, since it doesn't set the React state.. How can I enter my desired data into these React components in my Tampermonkey script?

Andrio
  • 1,852
  • 2
  • 25
  • 54
  • 1
    Just to clarify, I can't modify the React components at all. This is purely for a tampermonkey script running on the client. – Andrio Aug 31 '18 at 19:18
  • 2
    https://stackoverflow.com/a/53797269 has the answer to this. It helped me out a lot. – trumad Jun 11 '19 at 08:53

4 Answers4

7

The answer could be found there https://github.com/facebook/react/issues/11488#issuecomment-347775628

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);
fengxing
  • 374
  • 1
  • 4
  • 10
4

React doesn't expose component instances, so they aren't reachable without tampering an application on initialization, if this is possible.

Input values should be changed like they would be with vanilla JavaScript, by emitting DOM events.

React provides utility library that has helper functions to do that.

Here's an example. An input:

<input id="input" value={this.state.name} onChange={e => this.setState({ name: e.target.value })} />

And user script that runs after React application initialization:

import { Simulate } from 'react-dom/test-utils';

const input = document.getElementById('input');
input.value = 'Foo';
Simulate.change(input);
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • That utility library looks like what I need, but I'm not sure if it's possible to import that into my Tampermonkey script. – Andrio Sep 12 '18 at 17:03
  • 1
    @Andrio Most React libs are UMD modules that are exposed as globals in non-modular environment. This one should be available `window.ReactTestUtils` on `react-dom` load. – Estus Flask Sep 12 '18 at 17:07
2

There's some good answers here, but none that work with TextArea. Here's a generic method stolen from another SO post which handles more cases:

const inputTypes = [
    window.HTMLInputElement,
    window.HTMLSelectElement,
    window.HTMLTextAreaElement,
];

export const triggerInputChange = (node, value = '') => {

    // only process the change on elements we know have a value setter in their constructor
    if ( inputTypes.indexOf(node.__proto__.constructor) >-1 ) {

        const setValue = Object.getOwnPropertyDescriptor(node.__proto__, 'value').set;
        const event = new Event('input', { bubbles: true });

        setValue.call(node, value);
        node.dispatchEvent(event);

    }

};
Dr-Bracket
  • 4,299
  • 3
  • 20
  • 28
-1
class HelloWorld extends React.Component{
  constructor(props){
    super(props);
    this.state = {firstname: ''};
    this.handleChange = this.handleChange.bind(this);
  }
  handleChange(e){
   this.setState({firstname: e.target.value})
  }
  render(){
   return(<div><input id="firstname" type=text onChange={(e) =>this.handleChange()} value={this.state.firstname} ></div>) 
 }
}
abhishek
  • 1
  • 1