最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Correct way to programmatically fill a ReactJS input - Stack Overflow

programmeradmin2浏览0评论

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.

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.

Share Improve this question asked Nov 21, 2017 at 11:36 user000111181user000111181 4237 silver badges20 bronze badges 7
  • Do you by chance have a link to the page you are trying to crawl? – quickshiftin Commented Nov 30, 2017 at 17:19
  • Sorry that's behind authentication – user000111181 Commented Dec 1, 2017 at 10:23
  • OK, I think I was able to provide a good answer any way; hope it helps! – quickshiftin Commented Dec 1, 2017 at 20:12
  • is there any plunkr, or fiddle that can clarify the issue, so that people can dig right into the code? – ProllyGeek Commented Dec 2, 2017 at 1:21
  • @ProllyGeek Checkout the snippet in my answer, which is pretty much the same as this codepen. I think I threw a few more console.log() calls in the snippet though. – quickshiftin Commented Dec 2, 2017 at 4:31
 |  Show 2 more ments

3 Answers 3

Reset to default 2 +50

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 ponent 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 mon 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
      };
  },
  ponentDidMount: function() {
    console.log('react mounted');
  },
  // Imagine something happens inside react (AJAX response es in for example),
  // causing the nodes of interest to be rendered in DOM
  ponentWillMount: 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./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare./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.

I can imagine you simply can't. It depends how it is implemented and what events it is listening to.

If React Component is controlled and registering 'onChange' event, then it might be possible.

If it's uncontrolled ponent, then I guess it might not work. Even if you fill input somehow. On form submit, it might be still using value from it's internal state.

https://reactjs/docs/uncontrolled-ponents.html

https://reactjs/docs/forms.html#controlled-ponents

first of all verify that the element exists with a vanilla if. If it exists then programatically fill it it. Note some UI libraries might require an initialization or upgrade upon dynamic add of value.

if(document.getElementById("my-input")){
  document.getElementById("my-input").value = "foo";
}

If you're adding elements with scripting, you will probably need to attach event listeners after the element is created.

发布评论

评论列表(0)

  1. 暂无评论