Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way to programmatically fill a ReactJS input

I am building a headless crawler running JavaFX Webkit, it definitely is not as powerful as chrome's v8.

However I've run into problems lately, wherein I am trying to input a value to react rendered input fields.

Here's what I have done till now and failed.[ Note: I don't have control over the source / React code. Since I am trying to crawl a destination site ]

  1. jQuery - $('input.r_input').val("2");
  2. Vanila JS - document.querySelector("input.r_input").value = "2";
  3. Trigger change events through jquery trigger - change, blur , keyup, keydown, etc.
  4. Creating a manual event like :

    event = new Event( event, {target: obj, bubbles: true} ); event.simulated = true; return obj ? obj.dispatchEvent(event) : false;

and triggering an input event.

None of the above works.

I am adding parts of react code from the JS file on the website if it may help to add some more context.

Create:

t.prototype.createInputProps = function(e) {
    return {
        disabled: this.props.submitting || e > this.focusIndex,
        className: "r_input",
        type: "tel",
        name: "tan-" + e,
        maxLength: 1,
        pattern: "[\\d]*",
        tabIndex: 0,
        placeholder: "·",
        autoComplete: "off"
    }
}

Render :

t.prototype.render = function() {
    var e = this.props,
        t = e.meta,
        n = t.touched,
        r = t.error,
        o = (e.input.value, sa()("r_input", {
            "has-error": r && n
        }));
    return us("article", {
        className: o
    }, void 0, us("div", {
        className: "r_inputs"
    }, void 0, ro.a.createElement("input", as({
        onPaste: this.handleOnPaste,
        ref: this.addInputToList,
        onKeyUp: this.handleKeyUp,
        value: this.getValue(0)
    }, this.createInputProps(0))), ro.a.createElement("input", as({
        ref: this.addInputToList,
        onKeyUp: this.handleKeyUp,
        value: this.getValue(1)
    }, this.createInputProps(1))), ro.a.createElement("input", as({
        ref: this.addInputToList,
        onKeyUp: this.handleKeyUp,
        value: this.getValue(2)
    }, this.createInputProps(2))), ro.a.createElement("input", as({
        ref: this.addInputToList,
        onKeyUp: this.handleKeyUp,
        value: this.getValue(3)
    }, this.createInputProps(3))), ro.a.createElement("input", as({
        ref: this.addInputToList,
        onKeyUp: this.handleKeyUp,
        value: this.getValue(4)
    }, this.createInputProps(4))), ro.a.createElement("input", as({
        ref: this.addInputToList,
        onKeyUp: this.handleKeyUp,
        value: this.getValue(5)
    }, this.createInputProps(5)))), n && r && us(is.a, {}, void 0, r))
}

Not sure If I need to add handleKeyUp, but that contains some validation code.

Any help will be appreciated.

like image 948
user000111181 Avatar asked Nov 21 '17 11:11

user000111181


People also ask

How do you autofill input fields in React?

The React AutoComplete supports the autofill behavior with the help of autofill property. Whenever you change the input value and press the down key, the AutoComplete will autocomplete your data by matching the typed character.


1 Answers

In your case, it looks like you can fire a keyup event on the element after changing the value. You may also have to wait for the element to appear in the DOM, based on a React state change. Try this

setTimeout(function() {
    var event = new Event('keyup', { bubbles: true });
    $('input.r_input').val("2")[0].dispatchEvent(event);
}, 3000);

It will of course be hard to provide something definitive without access to the page in question.

Note: I came up with the voodoo to dispatch a keyup event via this thread which says the React onChange handler listens for input events and by looking at the code you provided which calls onKeyUp. My sample code uses onChange handler in React & dispatches input events as prescribed in the linked thread.

Also Note: I failed to get this working with jQuery's trigger method, maybe you can figure out what I was missing there, but event dispatching with native Javascript is working in my snippet below.

Read on for details!


There is no single answer for your question, because how a given React component reads in values from input fields can vary, so you'll need to look at each React implementation you are trying to manipulate. I'd hazard a guess that onChange is the most common way for React to capture input in a text field. The React onChange handler listens for input events.

It's also possible React's state is updating to cause the elements you are looking for to be added to the DOM after jQuery's .ready() callback fires. This would mean your selectors aren't finding anything because the elements they are looking for are not present in the DOM when they are looking. (This is why I added the setTimeout in my suggested solution).

Here I illustrate the issue of elements appearing in the DOM after a React state change, and a working example where jQuery is used to set the value of an input element managed by React. (Make sure to look at the console output when you run it to get an idea what's happening), you should see this in the HTML when it's all finished

React thinks the value is 50

jQuery(function() {
  console.log('jquery loaded');
  
  function searchForNodeOfInterest() {
    console.log('jQuery searching for nodes of interest');
    if(jQuery('#interesting-node').length === 0) {
      console.log('jQuery did not find the selector of interest');
    } else {
      console.log('jQuery found the selector of interest, setting input value...');
      var event = new Event('input', { bubbles: true });
      jQuery('#interesting-node').val(50)[0].dispatchEvent(event);
    }
  }
  
  searchForNodeOfInterest();
  
  setTimeout(searchForNodeOfInterest, 4000);
  console.log('jQuery - taking a nap, I will search again soon...');
});

var App = React.createClass({
  getInitialState: function() {
      return {
        hidden: true
      };
  },
  componentDidMount: function() {
    console.log('react mounted');
  },
  // Imagine something happens inside react (AJAX response comes in for example),
  // causing the nodes of interest to be rendered in DOM
  componentWillMount: function() {
    var that = this;
    setTimeout(function() {
      console.log('react updating the state...');
        that.setState({
          hidden: false,
          value: null
        })
    }, 2000);
  },
  handleInput: function(e) {
    console.log('react handling input...', e.target.value);
    this.setState({
      value: e.target.value
    })
  },
  render: function() {
    return this.state.hidden ? null : <form>React Powered Input: <input type="text" id="interesting-node" onChange={this.handleInput}/><p>React thinks the value is {this.state.value}</p></form>
  }
});


ReactDOM.render(<App />, document.getElementById('react'));
<body>
  <div id="react"></div>
</body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<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>

A more reliable technique than setTimeout for some random amount of time is a wrapper that periodically checks for the presence of a selector, like this.

like image 136
quickshiftin Avatar answered Nov 08 '22 14:11

quickshiftin