16

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?

2
  • 2
    Just to clarify, I can't modify the React components at all. This is purely for a tampermonkey script running on the client. Commented Aug 31, 2018 at 19:18
  • 3
    stackoverflow.com/a/53797269 has the answer to this. It helped me out a lot. Commented Jun 11, 2019 at 8:53

4 Answers 4

14

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);
Sign up to request clarification or add additional context in comments.

1 Comment

Is there a way to make it work with <select> as well?
6

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);

    }

};

1 Comment

Great but I can't get this to work for a select yet.
5

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);

2 Comments

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 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.
1

A modern update to an old question. Here's a version that works with React 17.

function setReactInputValue(el, value) {
    // Store the last value to update React's internal tracker
    const last = el.value;
    // Use the native value setter to change the input's value
    const nativeInputValueSetter = Object.getOwnPropertyDescriptor(
        window.HTMLInputElement.prototype,
        'value'
    ).set;
    nativeInputValueSetter.call(el, value);
    // Update React's internal value tracker
    const event = new Event('input', { bubbles: true });
    const tracker = el._valueTracker;
    if (tracker) {
        tracker.setValue(last);
    }
    // Dispatch the input event to notify React of the change
    el.dispatchEvent(event);
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.