briangervais
briangervais

Reputation: 31

How do I get onClick to work in my nested React Component?

UPDATE: My code checked out just fine. The issue was that a stopPropagation() was called in a separate Javascript library that prevented my onClicks from working.

--

I have a "MenuLink" react component in which I've added an onClick listener to an 'a' tag. The "MenuLink" component is imported from a "MenuItem" component which is imported from a "MainMenu" component (see below).

When I click on link generated from MenuLink, nothing happens. No errors, no nothing. I would expect to see "handleClick" in my console and for the link to be prevented from executing.

MenuLink.js


class MenuLink extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = props.link;
  }

  handleClick(e) {
    console.log("handleClick");
    e.preventDefault();
  }

  render() {
    const link = this.state;

    return (
      <a
        href={link.alias}
        onClick={this.handleClick}
      >
        {link.title}
      </a>
    );
  }
}

export default MenuLink;

MenuItem.js

import MenuLink from './MenuLink.js';

class MenuItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = props.item;
  }

  render(key) {
    const item = this.state;

    return(
      <li
        key={key}
      >
        <MenuLink
          link={item}
        />

      </li>
    );
  }
}

export default MenuItem;

MainMenu.js

import MenuItem from '../components/MenuItem.js';

class MainMenu extends React.Component {
  state = {
    menu: []
  }

  render() {
    return(
      <ul className="menu">
        {this.state.menu.map(function(menuItem, i) {
          return(
            <MenuItem key={i} item={menuItem} />
          )
        })}
      </ul>
    );
  }

  componentDidMount() {
    fetch('/api/menu_items/main')
    .then(res => res.json())
    .then((data) => {
      this.setState({ menu: data })
    })
    .catch(console.log)
  }
}

export default MainMenu;

Upvotes: 0

Views: 967

Answers (3)

Nafeo Alam
Nafeo Alam

Reputation: 4692

This approach should work

class MenuLink extends React.Component {
  constructor(props) {
    super(props);
    //this.handleClick = this.handleClick.bind(this);
    this.state = props.link;
  }

  handleClick = e => {
    console.log("handleClick");
    e.preventDefault(); 
  }

  render() {
    const link = this.state;

    return (
      <a
        href={link.alias}
        onClick={this.handleClick}
      >
        {link.title}
      </a>
    );
  }
}

export default MenuLink;

Upvotes: 0

Brian Thompson
Brian Thompson

Reputation: 14395

The below snippet shows that it does work as expected. No changes were made.

class MenuLink extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
    this.state = props.link;
  }

  handleClick(e) {
    console.log("handleClick");
    e.preventDefault();
  }

  render() {
    const link = this.state;

    return (
      <a
        href={link.alias}
        onClick={this.handleClick}
      >
        {link.title}
      </a>
    );
  }
}

class MenuItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = props.item;
  }

  render(key) {
    const item = this.state;
    
    console.log(key) // Undefined. Don't do this
    return(
      <li
        key={key}
      >
        <MenuLink
          link={item}
        />

      </li>
    );
  }
}

class MainMenu extends React.Component {
  state = {
    menu: [{
      alias: 'test', 
      title: 'test'
    },
    {
      alias: 'test2', 
      title: 'test2'
    }]
  }

  render() {
    return(
      <ul className="menu">
        {this.state.menu.map(function(menuItem, i) {
          return(
            <MenuItem key={i} item={menuItem} />
          )
        })}
      </ul>
    );
  }
}

ReactDOM.render(<MainMenu />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

Upvotes: 1

Khabir
Khabir

Reputation: 5862

Please check this example:

import React from "react";

class MenuLink extends React.Component {
    constructor(props) {
        super(props);
        this.handleClick = this.handleClick.bind(this);
        // this.state = props.link;
        this.state = {
            link: {alias: "http://www.google.com", title: 'Google'}
        }
    }

    handleClick(e) {
        console.log("handleClick");
        e.preventDefault();
    }

    render() {

        const link = this.state.link;

        return (
            <div>
                <a
                    href={link.alias}
                    onClick={this.handleClick}
                >
                    {link.title}
                </a>
            </div>
        );
    }
}

export default MenuLink;

Upvotes: 0

Related Questions