1

I have a situation when I have to render React controls in some code that I do not control. It's a kendo UI grid and I need to render custom React component in the grid cells.

Grid is created using jQuery under the hood. It also addes a click event listener for the parent html element (on the table row). In my React component I also have a click event listener. The problem is that the handler attached to the parent fires first.

The Snippet below (also available as a fiddle) demonstrates the issue. To keep this example as simple as possible I just use a div instead of the Kendo grid. The point is we have an event handler on the parent html element, then we render the React component into this parent element with its click handler, and the parent element takes precedence over React.

class Btn extends React.Component {

  constructor(props) {
    super(props);

    this.click = this.click.bind(this);
  }

  click() {
    alert('Btn');
  }

  render() {
    return (
      <span onClick={this.click}>Click me</span>
    );
  }
};

$(function() {

  $('#container').on('click', function() {
    alert('JS');
  });

  ReactDOM.render(
    <Btn />,
    document.getElementById('btn')
  );

});
<div id="container">
  <div id="btn">
  </div>
</div>

<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>
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Oleksandr Taran
  • 581
  • 5
  • 9
  • 1
    You can put your example **here, on-site** as a runnable Stack Snippet; they support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). That might help you get answers. – T.J. Crowder Aug 08 '17 at 14:34
  • (I went ahead and did it for you.) – T.J. Crowder Aug 08 '17 at 14:36
  • Do you want to keep both event listener? The simple solution could be to programmatically remove the event listener that bothers you. By the way, this could help you: https://stackoverflow.com/a/1369080/5860648 – sjahan Aug 08 '17 at 14:37

2 Answers2

1

It's unfortunate, but as near as I can tell you can't tell React to bind its handlers deeper in the tree (it uses document, then uses delegation from there).

To get around it, you'd do a kind of uncontrolled component (that's linked from Integrating with Other Libraries) and use a ref to hook your handler up:

First we set up the ref:

<span ref={span => this.span = span}>Click me</span>

Then we use lifecycle methods to add and remove our handler:

componentDidMount() {
  this.span.addEventListener("click", this.click);
}

componentWillUnmount() {
  this.span.removeEventListener("click", this.click);
}

Live example:

class Btn extends React.Component {

  constructor(props) {
    super(props);

    this.click = this.click.bind(this);
  }
  
  componentDidMount() {
    this.span.addEventListener("click", this.click);
  }
  
  componentWillUnmount() {
    this.span.removeEventListener("click", this.click);
  }

  click() {
    alert('Btn');
  }

  render() {
    return (
      <span ref={span => this.span = span}>Click me</span>
    );
  }
};

$(function() {

  $('#container').on('click', function() {
    alert('JS');
  });

  ReactDOM.render(
    <Btn />,
    document.getElementById('btn')
  );

});
<div id="container">
  <div id="btn">
  </div>
</div>

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

(If you need to support obsolete browsers without addEventListener, either use jQuery, or if you don't want the dependency this answer has a small cross-browser function for hooking events. You'd need to write your own unhook counterpart.)

You could even take it further and only hook up a single click handler, at the level of the element where you've mounted your component, and do your own event delegation (or use jQuery's) to dispatch as appropriate.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks. I think it's exactly what I need but I already did it with pure jQuery. Component was quite simple, so I just did all html creation and event handling manually. – Oleksandr Taran Aug 08 '17 at 15:44
0

This should not longer be a problem with the native implementation of the Kendo Grid for React. As the events that grid is listening to are attached using React, and not jQuery, so they follow the bubbling in React. Thus you can cancel them from the child if you want. See the source of this demo that have some custom components in custom cells.

Xizario
  • 481
  • 2
  • 9