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