ReactJS: onClick handler not firing when placed on a child component
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
.
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!
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!" />
)
}
});
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.
}
});