Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically fill input elements built with React?

I'm tasked with crawling website built with React. I'm trying to fill in input fields and submitting the form using javascript injects to the page (either selenium or webview in mobile). This works like a charm on every other site + technology but React seems to be a real pain.

so here is a sample code

var email = document.getElementById( 'email' );
email.value = '[email protected]';

I the value changes on the DOM input element, but the React does not trigger the change event.

I've been trying plethora of different ways to get the React to update the state.

var event = new Event('change', { bubbles: true });
email.dispatchEvent( event );

no avail

var event = new Event('input', { bubbles: true });
email.dispatchEvent( event );

not working

email.onChange( event );

not working

I cannot believe interacting with React has been made so difficult. I would greatly appreciate any help.

Thank you

like image 488
Timo Kauranen Avatar asked Nov 30 '16 17:11

Timo Kauranen


People also ask

How do you use React to set the value of an input?

To get the value of an input on button click in React: Declare a state variable that tracks the value of the input field. Add an onClick prop to a button element. When the button is clicked, update the state variable.

How do you auto populate a field in React?

<Input value={variable_name} .... Whenever you will update that variable, automatically that value will get populated in input element. Now you can populate some default value by assigning a value to variable_name and user can update that value by onChange function.


3 Answers

This accepted solution appears not to work in React > 15.6 (including React 16) as a result of changes to de-dupe input and change events.

You can see the React discussion here: https://github.com/facebook/react/issues/10135

And the suggested workaround here: https://github.com/facebook/react/issues/10135#issuecomment-314441175

Reproduced here for convenience:

Instead of

input.value = 'foo'; input.dispatchEvent(new Event('input', {bubbles: true})); 

You would use

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

and then

setNativeValue(input, 'foo'); input.dispatchEvent(new Event('input', { bubbles: true })); 
like image 147
meriial Avatar answered Oct 11 '22 09:10

meriial


React is listening for the input event of text fields.

You can change the value and manually trigger an input event, and react's onChange handler will trigger:

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>
like image 40
TimoStaudinger Avatar answered Oct 11 '22 07:10

TimoStaudinger


Here is the cleanest possible solution for inputs, selects, checkboxes, etc. (works not only for react inputs)

/**
 * 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 }));
}

Usage:

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)
like image 25
Dmytro Soltusyuk Avatar answered Oct 11 '22 09:10

Dmytro Soltusyuk