ReactJS: onClick handler not firing when placed on a child component

ReactjsReact Jsx

Reactjs Problem Overview


I recently started working with ReactJS. Specifically, I am utilizing the react-rails ruby gem and react-bootstrap components.

I have a question regarding placing onClick event listeners in child components.

As you can see from the code sample below, I have a parent component that 'calls' a child component in its render function. Within that render function, I have React onClick listener that calls handleToggle when it is clicked.

###* @jsx React.DOM ###

ToggleIconButton = React.createClass
  getInitialState: ->
    toggleOn: false
  handleToggle: (evt) ->
    this.setState(toggleOn: !this.state.toggleOn)
  render: ->
    `<IconButton onClick={this.handleToggle}>Toggle Button</IconButton>`

IconButton = React.createClass
  render: ->
    # BsButton and BsGlyphicon are React-Bootstrap components
    `<BsButton>
      <BsGlyphicon glyph={this.props.glyph} />
      {" " + this.props.text}
     </BsButton>`

Unfortunately, this doesn't work the way I thought it would. ToggleIconButton::handleToggle never gets called. Instead, the onClick listener is placed on IconButton and references ToggleIconButton::handleToggle.

React Dev Tool Screen


I don't want to add any additional behavior to IconButton. The way I see it, no conditional logic should be placed in IconButton. All it should do is represent an icon and button and not have to worry about any browser events. That's what ToggleIconButton is for.

While I know I could wrap a React Div around IconButton and write an onClick listener there (I've tried this and it works), it seems like that's a bit janky.

Does anybody know a good way of doing this? Thanks guys!

Reactjs Solutions


Solution 1 - Reactjs

So, the proper way to handle eventing in this case would be to pass the event handler down to the child component. There are a few ways to accomplish what you want, and I might implement this behavior differently (not sure what the use case is), but I wired up an example in JSX for you that demonstrates the typical event handling between Parent and Child Components. You can find it here...

JS Fiddle

Just think of it like this:

var ParentComponent = React.createClass({
    render: function(){
        return (
            <ChildComponent onSomeEvent={this.handleThatEvent} />;
        )
    },
    handleThatEvent: function(e){
         //update state, etc.
    }
});

var ChildComponent = React.createClass({
    render: function(){
        return (
           <input type="button" onClick={this.props.onSomeEvent} value="Click Me!" />
        )
    }
});

Solution 2 - Reactjs

You don't need to make a child component if before you call the node you create a var to reference the render's this i.e.

var self = this;

So for example (this is contrived and the var self isn't needed in this case, but in the case of nested return statements it would be required).

var ParentComponent = React.createClass({
    render: function(){
        var self = this;
        return (
            <input type="button" onClick={self.handleThatEvent} value="Click Me!" />;
        )
    },
    handleThatEvent: function(e){
         //update state, etc.
    }
});

Better yet, you could bind this to the function.

var ParentComponent = React.createClass({
    render: function(){
        return (
            this.state.array.map(function(){
              return (<input type="button" onClick={this.handleThatEvent} value="Click Me!" />);
            }.bind(this));
        )
    },
    handleThatEvent: function(e){
         //update state, etc.
    }
});

Or to follow captray's suggestion in the comments

var ParentComponent = React.createClass({
    render: function(){
        return (
            this.state.array.map(function(){
              return (<input type="button" onClick={this.handleThatEvent} value="Click Me!" />);
            }, this);
        )
    },
    handleThatEvent: function(e){
         //update state, etc.
    }
});

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionKurt MuellerView Question on Stackoverflow
Solution 1 - ReactjscaptrayView Answer on Stackoverflow
Solution 2 - Reactjsvipero07View Answer on Stackoverflow