meriial
meriial

Reputation: 5136

How do I programmatically trigger <select> onChange in React component with plain DOM Javascript?

I have a simple React component:

state = {
  value: '',
};

render() {
  return <select onChange={this.handleChange} value={this.state.value}>
    <option value="">--</option>
    <option value="1">One</option>
  </select>;
}

handleChange = (e) => {
  this.setState({
    value: e.target.value,
  });
  console.log('Handling Change!!')
};

I want to be able to trigger the event handler programmatically via ordinary javascript or, in this example, jQuery.

$select = $('select');
$select.val('1');
// then
$select.trigger('change');
// or
$select.trigger('input');

But none of these trigger the onChange handler in the React component, and therefore the state doesn't get updated.

My question: How can I trigger the onChange handler using the DOM and Javascript?

[edit]

To clarify a bit further, I am mounting the React Component like this:

el = document.createElement('div')
ReactDOM.render(MySelect, el)

$select = $(el).find('select');
$select.val('1');
// then
$select.trigger('change');
// or
$select.trigger('input');

Upvotes: 4

Views: 5352

Answers (1)

meriial
meriial

Reputation: 5136

Answering my own question, for posterity.

The issue, it turned out, was twofold. But first, some background:

React Events

The React event system listens for native DOM events by delegating to the root node. I.e. it doesn't attach listeners to each element with onChange, but just listens on the root document for events that bubble up through the DOM.

For those familiar with jQuery delegated events, this would be analogous to something like

$(document).on('change', '.my-select', callback)

jQuery Events

Secondly, jQuery itself somewhat 'overrides' the DOMs native event bubbling with its own system, which is how you can throw around custom events.

#The solution...#

As mentioned, I had 2 problems, and hence 2 solutions:

##First##

Because of jQuery using its own event system, events fired with

$(select).trigger('change')

will not reach React's root event listener. So we go native with...

select.dispatchEvent(new Event('change', {bubbles: true}));

...but that's still not enough.

##Second##

DOM events bubble up from the element to the document root. BUT notice that I never attached my element to the document!! The change event bubbles up to... nowhere! Again, it never reaches the React event listener registered on the document.

If I add my element to the DOM like this:

el = document.createElement('div')
ReactDOM.render(MySelect, el)
document.body.appendChild(el) // <-- attach to DOM

Now the event reaches the React event listener, and the original onChange handler in my component is fired.

I hope this helps anyone who comes across something similar.

ps. I found the documentation in this reactjs file helpful in understanding the React event system: https://github.com/facebook/react/blob/master/packages/react-dom/src/events/ReactBrowserEventEmitter.js

Upvotes: 11

Related Questions