46

I am trying to call a function when a user clicks a div (using onClick in react). I don't need to pass any arguments at this moment, just need to call the function. I'm fairly new to react.js so apologies in advance for my ignorance. Thanks.

var Test = React.createClass({

btnTapped: function(){
    console.log('tapped!');
},
render: function() {
    var stationComponents = this.props.stations.map(function(station, index) {

    return <div onClick={btnTapped()}><img src="img/test.png" />{station}</div>;

    });
    return <div>{stationComponents}</div>;
   }
});

var cards = ["amazon", "aeo", "aerie", "barnes", "bloomingdales", "bbw","bestbuy", "regal", "cvs", "ebay", "gyft", "itunes", "jcp", "panera", "staples", "walmart", "target", "sephora", "walgreens", "starbucks"];

ReactDOM.render(<Test stations={cards} />, document.getElementById('test-div'));
2
  • try with: onClick={this.btnTapped.bind(this)} Commented Jul 15, 2016 at 17:26
  • 5
    this.btnTapped() is the return value of this.btnTapped , you have to write it write it without parentheses Commented Jul 15, 2016 at 18:05

13 Answers 13

30

If your build system has support for babel, Use ES6 arrow functions in your react code.

If you are using ES6 class for creating components, use method binding at the constructor level to avoid binding at every render call and also provide a key to the div tag inside the map function.

class Test extends React.Component {
    constructor(props) {
        super(props);
        this.btnTapped = this
            .btnTapped
            .bind(this);
    }
    btnTapped() {
        console.log('tapped');
    }
    render() {

        return (
            <div>
                {this
                    .props
                    .stations
                    .map((station, index) => {
                        return <div key={index} onClick={this.btnTapped}>{station}</div>
                    })
                }
            </div>
        )
    }
}

var cards = ["amazon", "aeo", "aerie", "barnes", "bloomingdales", "bbw", "bestbuy", "regal", "cvs", "ebay", "gyft", "itunes", "jcp", "panera", "staples", "walmart", "target", "sephora", "walgreens", "starbucks"];

    
ReactDOM.render(
    <Test stations={cards}/>, document.getElementById('test-div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<body>
  <div id="test-div"></div>
</body>

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

1 Comment

This answer fails to mention the main bug in the OP's code: calling onClick={btnTapped()} rather than just naming it, onClick={btnTapped}. That's not the whole story, as explained elsewhere, but it's the most important point.
23

You should set a function to onClick attribute, not call it. Should be: onClick={this.btnTapped} instead of onClick={btnTapped()}.

Also, it is possible to do like this:

<div 
  onClick={function(e) {
    this.btnTapped(); //can pass arguments this.btnTapped(foo, bar);          
  }}
 >

It's usually used when you need to pass an argument to your function.

Also, to be able to get component's context from the external function, you should use bind(this) method. Like: onClick={btnTapped.bind(this)}

And since you are using a scope function for mapping array, new context is created inside of: this.props.stations.map(function(station, index){}. And this is overridden. Just use an arrow function instead:

var stationComponents = this.props.stations.map((station, index) => {

   return <div onClick={this.btnTapped}><img src="img/test.png" />{station}</div>;

});

3 Comments

I get this error still: embedded:29 Uncaught TypeError: Cannot read property 'btnTapped' of null
Note that if they can't support fat arrows, they can bind the context to the map function .map(function(..) {}.bind(this))
idk why this work but ()=> {...} don't >.>
19

Define your button component

// Button.jsx
import React from 'react';

const Button = (props) => {
    return <button onClick={props.onClick}>{props.children}</button>
}

export default Button;

and use it like this

// app.jsx
function handleClick() {
    console.log("Button clicked");
  }


<Button onClick={handleClick}>Click me</Button>

2 Comments

Any explanation for this!?
I don't think this is necessary. It may be neater in some cases but you can define a function within a component - there's a bunch of examples here where the function is defined in line. react.dev/learn/responding-to-events#adding-event-handlers
19

This is somewhat a noob suggestion but please check the spelling of your onClick() function, I had spelt it wrong like onclick() and It took me a good hour to find it out.

3 Comments

this question already has an accepted answer so this is not a really usefull suggestion, probably more suitable if this is placed in the comments
@Anonymouse To be fair, you can't comment before having 50 reputation. Shashi Shekhar If you don't want the reputation penalty, I suggest you to simply delete your answer. You can post a new one taking Anonymou's feedback in consideration if you think it's relevant. Good luck on stackoverflow ! :)
Nah, I'll leave the answer.
6

This is an old post, still posting my answer as I faced same issue in 2021 and none of the answers above helped.

I added bundle.js in my index.html file. And html-webpack-plugin was also adding it for me. Loading bundle.js twice seemed to have caused the issue. It worked fine after removing that reference from index.html

Comments

3

For those who are facing this issue, it may occur due to some CSS issue as well

For me, it worked by removing the CSS property named "pointer-events". In my CSS, it was set to none. i.e.

pointer-events: none;

I just removed it from my CSS and the click event starts to work in React!

Comments

2

You missed this keyword before function. Also you must provide function but not calling it

<div onClick={this.btnTapped}>

UPDATE:

You missed that you are redefines this in map callback function.

Use arrow function

this.props.stations.map((station, index) => {
  return <div onClick={this.btnTapped}><img src="img/test.png" />{station}</div>;
});

or bind context to function

this.props.stations.map(function (station, index) {
  return <div onClick={this.btnTapped}><img src="img/test.png" />{station}</div>;
}.bind(this));

1 Comment

This doesn't seem to work at all... The function isn't invoked and the message isn't logged to the console as a result. Uncaught TypeError: Cannot read property 'btnTapped' of null
2

Whenever you want to use a function in the render method, first you need to bind those methods in the constructor. One more thing is when you don't want to pass any arguments to the methods that you need to call use this onClick = {this.btnTapped} instead of onClick = {this.btnTapped()}. Thank you.

import React, {Component} from 'react';
import ReactDOM from 'react-dom';

export default class Test extends Component {
constructor (props) {
    super(props);
//set cards value in state then you don't need to use props...
    this.state = {
        cards: ["amazon", "aeo", "aerie", "barnes", "bloomingdales", 
        "bbw","bestbuy", "regal", "cvs", "ebay", "gyft", "itunes", 
        "jcp", "panera", "staples", "walmart", "target", "sephora", 
        "walgreens", "starbucks"]
    }
    this.btnTapped = this.btnTapped.bind(this);
    // bind all the methods which you're using the "this.state" inside it....
}

btnTapped (card) {
    console.log("Coming here:::" + card)
}

render() {
    var cards = this.state.cards
    return (
        <div>
            {/* if you don't want to pass an arguments use this... */}
            {
                cards.map((card, i) => 
                    <button key = {i} onClick = {this.btnTapped}>{card}</button>
                )
            }
            {/* if you want to pass arguments use this  */}
            {
                cards.map((card, i) =>
                    <button key = {i} onClick = {(e) => this.btnTapped(card)}>{card}</button>
                )
            }
        </div>
    );
}
}
ReactDOM.render(<Test />, document.getElementById('test-div'));

Comments

2

Just my 2 cents for someone whose issue is not resolved with this thread's answer. I face the similar issue and after debugging I found that my button had a 'z-index' of -9999 so it was behind a layer and hence the click was not reaching it. I changed the z-index to a positive value of 1 and the click got captured.

Comments

-1

If you are using webpack, make sure your output file which you configured in webpack.config.js (usually bundle.js) is not added in your index.html file.

This solved the problem for me.

Comments

-1

In my case the div containing the button was wrapped by an outer div. Because of which the events were not getting triggered. I also had an input box alongside the button, Because of that outer div, I was also not able to click on the input box.

In order to understand what was causing the problem, I added a red border to the surrounding divs and then I came to know about the outer div which was wrapping my main div.

So my suggestion for such kind of problem would be to add a border to nearby elements like

border: '1px solid red'

And then try to figure out what is restricting the events.

Comments

-2

avoid classic JS method like

test(){ console.log('test') }

instead use callback affectation like

test = () => { console.log('test') }

tell me if its help

2 Comments

why ? what's the difference ?
@polkduran It doesn't matter in this case, but for your future reference, function functions do not have this attached to them (the context of your code surrounding it if you will), arrow functions () => {} do, it generally causes less problems so to speak.
-2

sometimes its just an error just restart the server and it will work .

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.