Reputation: 1048
I have a pretty simple piece of React that loos over an array and outputs tab names. However my click handler no longer works (it worked before when I didn't have the loop).
The difference between this piece and before I had the .map loop is that this new piece has two returns in the render function. One for the outer div element that React requires, then one for the looping over the objects.
Does anyone how I can successfully get the click handler working again please?
Please see my component
class TabMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
};
this.tabMenuList = [
{
title: 'My Account',
section: 'MyAccount'
},
{
title: 'Conference Details',
section: 'MyAccount'
},
{
title: 'My Abstract',
section: 'MyAbstract'
}
];
}
handleClick(e){
e.preventDefault();
console.log('this is the click handler', this);
ReactDOM.render(<Conference />,document.getElementById('content'));
}
render() {
return (
<div>
{this.tabMenuList.map(function(menuItem, index){
return(
<li data={menuItem.section}>
<a onClick={this.handleClick.bind(this)} href={'#'}>
<img src={'assets/img/mail_icon.jpg'} />
<span>{menuItem.title}</span>
</a>
</li>
)
})}
</div>
);
}
}
Upvotes: 1
Views: 1999
Reputation: 8784
Solution
Use an ES6 arrow
function like so:
class TabMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
};
this.tabMenuList = [
{
title: 'My Account',
section: 'MyAccount'
},
{
title: 'Conference Details',
section: 'MyAccount'
},
{
title: 'My Abstract',
section: 'MyAbstract'
}
];
}
handleClick(e){
e.preventDefault();
console.log('this is the click handler', this);
ReactDOM.render(<Conference />,document.getElementById('content'));
}
render() {
return (
<div>
{this.tabMenuList.map((menuItem, index) => {
return(
<li data={menuItem.section}>
<a onClick={this.handleClick.bind(this)} href={'#'}>
<img src={'assets/img/mail_icon.jpg'} />
<span>{menuItem.title}</span>
</a>
</li>
)
})}
</div>
);
}
}
Why?
In your React code, this
is not referencing TabMenu
.
When declaring this
within a function, it automatically defaults to the global object - Window
in the case of your environment.
Since the following code is not in strict mode, and because the value of this is not set by the call, this will default to the global object.
However, it's important to know that
In strict mode, however, the value of this remains at whatever it was set to when entering the execution context, so, in the following case, this will default to undefined.
Why? Because according to this question and the first answer, ES6 modules use strict
by default and thus this
within your function
equals undefined
.
Therefore,
In arrow functions, this is set lexically, i.e. it's set to the value of the enclosing execution context's this. In global code, it will be set to the global object
Your enclosing execution context is TabMenu
.
MDN have a great article on this
and how it varies depending on the context in which this
is called in.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/this
Upvotes: 2
Reputation: 1825
Try this
<li data={menuItem.section}>
<a onClick={this.handleClick.bind(this)} href={'#'}>
<img src={'assets/img/mail_icon.jpg'} />
<span>{menuItem.title}</span>
</a>
</li>
instead of
<li data={menuItem.section}>
<a onClick=onClick={this.handleClick.bind(this)} href={'#'}>
<img src={'assets/img/mail_icon.jpg'} />
<span>{menuItem.title}</span>
</a>
</li>
or you can remove second return may be it will work
Upvotes: 0