1

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>
  );
}
}
7
  • 3
    you need to use a fat arrow on your tabMenuList map callback Commented Mar 27, 2017 at 13:29
  • this is NOT what you think it is inside of your .map function, as suggest above you need an arrow function. If you console.log(this), you'll see it is referencing Window Commented Mar 27, 2017 at 13:31
  • You may also need to change onClick=onClick= to just one onClick= Commented Mar 27, 2017 at 13:34
  • Thanks for the quick replies, trying a few arrow functions but not working just yet. Would someone please mind showing me what the arrow function would look like please? I also removed the double onClick. That wasn't always there :) . Commented Mar 27, 2017 at 13:39
  • 1
    {this.tabMenuList.map((menuItem, index) => { Commented Mar 27, 2017 at 13:45

2 Answers 2

2

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

Sign up to request clarification or add additional context in comments.

Comments

0

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

2 Comments

Thanks for the reply but I can't see any difference in the first suggestion?
sorry I forgot to edit answer, now you can check. you wrote onClick two times

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.