Creating anchor with onClick that React handles

Andy Gauge picture Andy Gauge · Dec 20, 2017 · Viewed 11.6k times · Source

I have a React Component that renders a <ul> and inserts <li> elements based on state.

class SimpleComponent extends React.Component {
  constructor(props) {
    this.state = { menu_items: [name: "First", id: 10] }
    this.clickMenu = this.clickMenu.bind(this)
  }
  generateLinks() {
    let to_return='';
    for (var i=0;i<this.state.menu_items.length; i++) {
      to_return = to_return + `<li><a onclick={clickMenu}> ${this.state.menu_item[i]['name']} </a></li>`
     }
     return to_return;
   }
   clickMenu(e) {
     console.log(e.target)
   }
   render() {
     return(
       <ul dangerouslySetInnerHTML={this.generateLinks()}></ul>
     )
   }
}

When I click on the Anchor, the console indicates Uncaught ReferenceError: clickMenu not defined. I've tried using this.clickMenu instead, but nothing occurs. I've noticed that the rendered anchor looks like:

<a onclick="{menuClick}">

Is there a way to create these anchor elements to have React pick up the onClick definitions rather than passing them to the browser to interpret?

Answer

Chris G picture Chris G · Dec 20, 2017

With React you're supposed to build components and subcomponents, not compose long HTML strings. You're basically undermining React's usage pattern, which is precisely the reason why the attribute you're using contains the word "dangerously".

Here's one way how to implement that list:

class SimpleComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { menu_items: [{ name: "First", id: 10 }] };
    this.clickMenu = this.clickMenu.bind(this);
  }
  clickMenu(id) {
    console.log(id);
  }
  render() {
    var items = this.state.menu_items.map(item => {
      let handleClick = () => {
        this.clickMenu(item.id);
      };
      return (
        <li key={item.id}>
          <a onClick={handleClick}>
            {item.name}
          </a>
        </li>
      );
    });
    return <ul>{items}</ul>;
  }
}

I'm building an array of <li> elements by mapping state.menu_items. Since the component is re-rendered when state changes occur, this will always update according to the current state (and only make changes that are actually necessary, which is one of React's defining characteristics).

Also note that your constructor was missing the super(props) call, and your state array's single element wasn't wrapped in curly braces.